3

テンプレートクラスで宣言されたフレンド演算子と、次のようなクラスの明示的なインスタンス化に問題があります。

template<class T>
class IneqCmp{
    friend bool operator!=(T a, T b){
        return !(a==b);
    }
};

struct IntCont{
    int x;
    bool operator==(IntCont that) const{
        return x==that.x;
    }
};
template class IneqCmp<IntCont>;
int main(){
    IntCont a{1}, b{2};
    while(a!=b);
}

operator!=(IntCont, IntCont) は IneqCmp<IntCont> のインスタンス化の時点でインスタンス化する必要があると思いますが、そうではありません。なんで?

4

1 に答える 1

6

上記のコードの問題は、が定義されているかどうかではなく、ルックアップ*operator!=によって決して見つからないという事実です。

関数をクラスのフレンドとして宣言すると、宣言は名前空間レベルの関数を宣言するという意味で奇妙ですが、その宣言は、囲んでいる型の ADL を介してのみ使用できます。yourは2 つの引数のいずれもoperator!=とらないため、事実上、ルックアップで見つけることは不可能です。IneqComp

operator!=一連の型で利用できるのデフォルトの実装を提供したい場合は、さまざまな方法で問題に取り組むことができます。

IneqCmpまず、継承を使用して ADL セットに追加できます。

struct IntCont : IneqCmp<IntCont> {
   // ...
};

IneqCmp<IntCont>ADL は引数のクラスと基本クラスを検索するため、内部でルックアップが効果的にトリガーされ、friend宣言とフリー関数が検索されます。

もう 1 つの方法は、ジェネリックoperator!=が定義される名前空間 (他の方法では見つからないようにするため) とtag型を追加することです。次に、そのタグから継承します。

namespace inequality_comparable {
   struct tag {};
   template <typename T>
   bool operator!=( T const & a, T const & b ) {
      return !(a==b);
   }
}
struct IntCont : inequality_comparable::tag {
};
bool operator==( IntCont const & a, IntCont const & b ) {
   return ...;
}
int main() {
    IntCont a,b;
    std::cout << a != b << "\n";
}

ここでの秘訣はinequality_comparable::tag、 が型のベースであるためinequality_comparable、ルックアップに名前空間を追加することです。コンパイラが遭遇a != bすると、現在のスコープで定義されたクラス、クラスとそのベース、およびそのベースが定義されている名前空間mainを使用しようとします。operator!=IntContIntCont


*オペレーターが実際に生成されたことを確認したい場合は、追加してみてください。

bool operator!=(IntCont a, IntCont b){
    return !(a==b);
}

インスタンス化のために演算子が定義されている場合、IneqCmpコンパイラ エラーが発生します。または、シンボルの生成されたオブジェクト ファイルをコンパイルして調べることもできます。

于 2012-08-17T21:01:40.593 に答える