Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

overloading operator<< for arrays

Today I thought it would be a nice idea to overload operator<< for C style arrays:

template<typename T, size_t N>
std::ostream& operator<<(std::ostream& os, T(&a)[N])
{
    os << '{' << a[0];
    for (size_t i = 1; i < N; ++i)
    {
        os << ',' << ' ' << a[i];
    }
    os << '}';
    return os;
}

int main()
{
    int numbers[] = {2, 3, 5, 7, 11, 13, 17, 19};
    std::cout << numbers << '\n';
}

Indeed, this prints {2, 3, 5, 7, 11, 13, 17, 19} nicely. However, by providing that overload, I cannot print string literals anymore:

    std::cout << "hello world\n";

error: ambiguous overload for 'operator<<' in 'std::cout << "hello world\012"'
note: candidates are:

note: std::basic_ostream<_CharT, _Traits>::__ostream_type&
std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _
Traits = std::char_traits<char>, std::basic_ostream<_CharT, _Traits>::__ostream_
type = std::basic_ostream<char>] <near match>

note:   no known conversion for argument 1 from 'const char [13]' to 'long int'

This is really puzzling. Why does the compiler even consider the long int overload when there is no conversion from const char[13] to long int in the first place?

Variations of this error message appear for long unsigned int, short int, short unsigned int, int, unsigned int, long long int and long long unsigned int.

(Other candidates are const void*, const char* and const _CharT*, and my own template.)


I solved the problem by providing the template for non-char types only:

template<typename T, size_t N>
typename std::enable_if<
    !std::is_same<typename std::remove_cv<T>::type, char>::value,
std::ostream&>::type operator<<(std::ostream& os, T(&a)[N])

But I'm still baffled by the question why the compiler considered the numeric types as candidates.

like image 520
fredoverflow Avatar asked Jan 29 '12 10:01

fredoverflow


2 Answers

The first stage of overload resolution is to identify the viable functions, which are those which can accept the number of arguments provided (completely ignoring types). (See eg 13.3.2 [over.match.viable]).

Then any needed conversions are considered to determine which is the unique best viable function.

In this case there is no such unique best (there's two equally good candidates).

The error message could just tell you the two ambiguous cases. But I think they're trying to be helpful by showing why all the other viable functions lost out. Sometimes this is useful, when you can't figure out why the function you wanted to be called hasn't been considered.

But I agree that mostly it's just a lot of noise, especially for functions like operator << or operator >> (or even operator []) which have a lot of overloads.

like image 65
Alan Stokes Avatar answered Oct 03 '22 05:10

Alan Stokes


The compiler is correct to reject the program. I think the key is that your overload and ostream::operator<<( char const * ) both appear in the error message. The integral ones are probably a red herring… you can reinterpret_cast a pointer (or a string literal) to long int (§5.2.10/4), but that's certainly not a standard conversion. Perhaps the compiler is just trying to be helpful by giving you more overloads.

Given your overload and the ostream member, overload resolution fails simply because there is no precedence rule to decide between them (§13.3.1.2). So, because the char const * member overload is the only one you might conflict with, your fix does seem appropriate.

like image 33
Potatoswatter Avatar answered Oct 03 '22 06:10

Potatoswatter