10

According to this answer, the correct way to overload output operator << for C-style arrays is this -:

#include <iostream>
using namespace std;

template <size_t arrSize>
std::ostream& operator<<( std::ostream& out, const char( &arr )[arrSize] )
{
    return out << static_cast<const char*>( arr ); // use the original version
}

// Print an array
template<typename T1, size_t arrSize>
std::ostream& operator <<( std::ostream& out, const T1( & arr )[arrSize] )
{
    out << "[";
    if ( arrSize )
    {
        const char* separator = "";
        for ( const auto& element : arr )
        {
            out << separator;
            out << element;
            separator = ", ";
        }
    }
    out << "]";
    return out;
}

int main()
{
    int arr[] = {1, 2, 3};
    cout << arr;
}

But I am still getting the compiler error

error: ambiguous overload for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'const char [2]')  

for the out << "["; and out << "]"; statements.

What is the correct way of doing this ?

Community
  • 1
  • 1
Anmol Singh Jaggi
  • 8,376
  • 4
  • 36
  • 77

1 Answers1

9

The problem is the standard overload for operator<< that prints a character array is this one:

template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
                                         const char* s );

So when you provide yours:

template <size_t arrSize>
std::ostream& operator<<( std::ostream& out, const char( &arr )[arrSize] )

That's going to be ambiguous: we have two different function templates with identical conversion sequences, neither of which is more specialized than the other.

However, since you want your version to JUST call the original, there is really no reason to provide your version at all. Just make your "generic" array printer not accept char using SFINAE:

// Print an array
template<typename T1, size_t arrSize, 
         typename = std::enable_if_t<!std::is_same<T1,char>::value>>
std::ostream& operator <<( std::ostream& out, const T1( & arr )[arrSize] )
{ /* rest as before */ }
Anmol Singh Jaggi
  • 8,376
  • 4
  • 36
  • 77
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Would you care to elaborate why the cited answer doesn't work? Did some overload disambiguation rule changed, or was something introduced, or was it never correct? It looks quite strange. Your approach is also valid for c++11, if you swap `enable_if_t` for the less handy `enable_if`, so I guess it would be a better approach overall, even for the other question. – luk32 Jun 30 '15 at 10:35
  • @luk32 It was never correct - there is no reason to prefer one overload over the other. – Barry Jun 30 '15 at 12:24
  • That was my guess, though I tend to doubt myself more than 200k+ user's accepted answer =). Thanks for clarification. – luk32 Jun 30 '15 at 12:33
  • @luk32: My reputation at the time was closer to 200 than to 200k :-( – Kerrek SB Jun 30 '15 at 13:14
  • 1
    @luk32: I fixed the answer. – Kerrek SB Jun 30 '15 at 13:25