2015-10-10 2 views
7

これは動作します:テンプレートクラスのネストされたクラスにフレンドオペレータの定義を提供するにはどうすればよいですか?

template<class Tim> 
struct Bob 
{ 
    struct Dave 
    { 
     Tim t{}; 
     friend bool operator < (const Dave& a, const Dave& b) 
     { 
      return a.t < b.t; 
     } 
    } d; 
}; 

これは動作しません:

template<class Tim> 
struct Bob 
{ 
    struct Dave 
    { 
     Tim t{}; 
     friend bool operator < (const Dave& a, const Dave& b); 
    } d; 
}; 

template<class Tim> 
bool operator < (const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b) 
{ 
    return a.t < b.t; 
} 

私は例えばマップでそれを使用しようとすると、私はリンカエラーを取得:

1>ConsoleApplication1.obj : error LNK2019: unresolved external symbol "bool __cdecl operator<(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)" ([email protected][email protected][email protected]@@[email protected]) referenced in function "public: bool __thiscall std::less<struct Bob<int>::Dave>::operator()(struct Bob<int>::Dave const &,struct Bob<int>::Dave const &)const " ([email protected]@[email protected]@@@[email protected]@[email protected][email protected]@@[email protected]) 

int main() 
{ 
    std::map<Bob<int>::Dave, int> v; 
    v[{}]; 
} 

どのように私は正しく外のクラス、この演算子を定義することができますか?

+0

'警告:friend宣言 'bool operator <(const Bob :: Dave&const Bob :: Dave&)'はテンプレート以外の関数を宣言しています。それについて) - あなたが宣言した友人の '演算子' 'と、さらに定義する'テンプレート<...>演算子 ''は同じものではありません。 – melak47

+0

@ melak47 VS2015警告レベル4、私は警告を受けていません。 –

+0

申し訳ありませんが、gccの出力を参照してください:http://coliru.stacked-crooked.com/a/bb58214d285f031f – melak47

答えて

4

通常、テンプレートクラスとfriend関数を前方宣言し、クラス定義内に特殊化を指定することで、このようなことを行います。しかし、この場合は簡単ではありません。従属型を使用すると、推論されないコンテキストにTimクラスが置かれますので、控除は失敗します。しかし、それを回避する方法があります:

#include <iostream> 
#include <type_traits> 
#include <map> 

template<class T> 
struct Bob; 

template<typename T, typename> 
bool operator < (const T& a, const T& b); 

struct DaveTag {}; 

template<class Tim> 
struct Bob 
{ 
    struct Dave : DaveTag 
    { 
     Tim t{}; 


     friend bool operator < <Bob<Tim>::Dave, void>(const typename Bob<Tim>::Dave& a, const typename Bob<Tim>::Dave& b); 
    } d; 
}; 

template<typename T, typename = typename std::enable_if<std::is_base_of<DaveTag, T>::value>::type> 
bool operator < (const T& a, const T& b) 
{ 
    return a.t < b.t; 
} 

struct X { 
    double t; 
}; 

int main() 
{ 
    std::map<Bob<int>::Dave, int> v; 
    v[{}]; 

    // This won't work 
    // X x, y; 
    //bool b = x < y; 

} 

基本的に、私はここでやったことは、コンパイラはoperator<のテンプレートパラメータとしてフルBob<Tim>::Daveを推測させています。しかし、明らかに単純な定義では、理解しにくい問題につながる可能性のあるタイプを推測できる可能性があります(T)。それを避けるため、小さなタグクラスDaveTagを追加しました。これは、Dave以外の理由で非常に一般的なoperator<のインスタンス化を防ぐことができます。

+0

私はここにあなたの努力に拍手喝采! –

+0

@NeilKirk問題ありません!私も楽しいしていた:) – Rostislav

+0

私は私の状況と少しシンプルに十分な回避策を思いついた:) –

0

これは十分な回避策です。私は、様々な理由(その時点で入手可能な定義はない、ここで起こっていくつかの明示的なテンプレートのインスタンス化)のためのクラスの演算子を実装したくなかったが、私は次のように生きることができます。今すぐ

struct Bob 
{ 
    struct Dave 
    { 
     Tim t{}; 

     static bool LessThan(const Dave& a, const Dave& b); 

     friend bool operator < (const Dave& a, const Dave& b) 
     { 
      return LessThan(a, b); 
     } 
    } d; 
}; 

静的関数LessThanは、通常の方法でクラスから実装できます。

関連する問題