5

ostream オーバーロードを理解しようとしています。このことを考慮

#include <iostream>

using std::ostream;

enum class A{a1, a2, a3};

template <class T>
ostream& operator<<(ostream& out, const T& a)
{
  switch(a)
    {
    case T::a1 :
      return out<<"a1";
    case T::a2 :
      return out<<"a2";
    case T::a3 :
      return out<<"a3";
    };
  return out;
}
/*ostream& operator<<(ostream& out, const A& a)                               
{                                                                              
  switch(a)                                                                    
    {                                                                          
    case A::a1 :                                                               
      return out<<"a1";                                                        
    case A::a2 :                                                               
      return out<<"a2";                                                        
    case A::a3 :                                                               
      return out<<"a3";                                                        
    };                                                                         
  return out;                                                                  
  }*/

int main()
{
  A a = A::a3;
  std::cout<<a<<std::endl;
}

コンパイル中に次のようなエラーが発生します

test.cpp:13:17: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘const char [3]’)
       return out<<"a1";
                 ^

通常の機能のコメントを外し、テンプレートバージョンにコメントを付けると正常に機能します。あいまいさが通常の機能にない理由と、テンプレート化されたバージョンにある理由

4

2 に答える 2

6

非テンプレート オペレーターはあいまいさを引き起こしません。これは、そのオペレーター自体がこの呼び出しを解決するのに適していないためです。

return out << "a1";
//     ^^^^^^^^^^^
//     This MUST be `std::operator <<`, no other valid overload of
//     operator << is found!

他の同様のものと同様に。

一方、テンプレート バージョンは、T具象型にバインドされていないため実行可能です。

template <class T>
ostream& operator<<(ostream& out, const T& a)
{
    switch(a)
    {
    case T::a1 :
      return out << "a1";
//           ^^^^^^^^^^^
//           Here the compiler could invoke std::operator <<
//           OR it could invoke your operator << template,
//           which is also viable since T could be anything!
//           Which one should it pick?

    // ...
    }
}

したがって、コンパイラは、名前空間または関数テンプレートのどちらでオーバーロードを選択するかを知りませんstd(はい、それは無限再帰を確立しようとする試みですが、コンパイラは気にする必要はありません)。

これらのオーバーロードはどちらも優れているため、あいまいです。

問題を解決する 1 つの方法は、テンプレートのオーバーロードを SFINAE 制約して、 が列挙型のoperator <<場合にのみオーバーロードの解決と見なされるようにすることです。T例えば:

#include <type_traits>

template <class T, 
    typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
ostream& operator<<(ostream& out, const T& a)

これが実際のです。

于 2013-06-19T13:39:33.460 に答える