Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Templatized ostream overload ambiguity error : basic_ostream<char> vs const char[]

Tags:

c++

templates

I am trying to understand ostream overloads. Consider this

#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;
}

While compiling i get error as below

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";
                 ^

While uncommenting normal function and commenting template version works fine. Why ambiguity is not in normal function and why it is in templatized version

like image 718
Abhishek Dixit Avatar asked Jun 19 '13 13:06

Abhishek Dixit


2 Answers

The non-template operator does not cause any ambiguity because that operator itself is not viable for resolving this call:

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

As well as the other similar ones.

The template version, on the other hand, is viable, since T is not bound to be any concrete type:

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?

    // ...
    }
}

Therefore, the compiler does not know whether to pick the overload in the std namespace or your function template (yes, that would be an attempt to establish an infinite recursion, but the compiler doesn't need to care).

Those overloads are both good, hence the ambiguity.

One way to fix your problem would be to SFINAE-constraint your template overload of operator << so that it is considered for overload resolution only when T is an enumeration type. For instance:

#include <type_traits>

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

Here is a live example.

like image 64
Andy Prowl Avatar answered Nov 14 '22 21:11

Andy Prowl


As Andy Prowl wrote in their answer, the problem is due to an unintentional overload ambiguity introduced by your code because there are now two suitable overloads for out<<"a1" (and also out<<"a2" and out<<"a3"), one from std and one being the very overload you've defined, which the compiler has a hard time choosing between.

An alternative solution, in addition to the one already described, would be to choose the desired overload explicitly with the using declaration:

template <class T>
ostream& operator<<(ostream& out, const T& a)
{
  using std::operator<<;
  switch(a)
    {
...

That will convey your intention to use the "standard" version of the function to the compiler, thus removing the ambiguity.

like image 31
undercat Avatar answered Nov 14 '22 22:11

undercat