Подтвердить что ты не робот

Перегрузка оператора << для массивов

Сегодня я подумал, что было бы неплохо перегрузить operator<< для массивов стилей C:

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

Действительно, это хорошо печатает {2, 3, 5, 7, 11, 13, 17, 19}. Однако, предоставляя эту перегрузку, я больше не могу печатать строковые литералы:

    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'

Это действительно озадачивает. Почему компилятор даже рассматривает перегрузку long int, когда нет преобразования от const char[13] до long int в первую очередь?

Варианты этого сообщения об ошибке появляются для long unsigned int, short int, short unsigned int, int, unsigned int, long long int и long long unsigned int.

(Другие кандидаты: const void*, const char* и const _CharT* и мой собственный шаблон.)


Я решил проблему, предоставив только шаблон для типов char:

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])

Но я все еще озадачен вопросом, почему компилятор рассматривал числовые типы в качестве кандидатов.

4b9b3361

Ответ 1

Первый этап разрешения перегрузки - это определение жизнеспособных функций, которые могут принимать количество аргументов (полностью игнорируя типы). (См., Например, 13.3.2 [over.match.viable]).

Затем учитываются любые необходимые преобразования, которые определяют единственную наилучшую жизнеспособную функцию.

В этом случае нет такого уникального лучшего (есть два одинаково хороших кандидата).

Сообщение об ошибке может просто рассказать вам два неоднозначных случая. Но я думаю, что они пытаются помочь, показывая, почему все другие жизнеспособные функции потеряны. Иногда это полезно, когда вы не можете понять, почему функция, которую вы хотите вызвать, не рассматривалась.

Но я согласен с тем, что в основном это просто шум, особенно для таких функций, как operator << или operator >> (или даже operator []), которые имеют много перегрузок.

Ответ 2

Компилятор правильно отклоняет программу. Я думаю, что ключ заключается в том, что ваша перегрузка и ostream::operator<<( char const * ) отображаются в сообщении об ошибке. Интегральные, вероятно, красная селедка... вы можете reinterpret_cast указатель (или строковый литерал) на long int (§5.2.10/4), но это, конечно, не стандартное преобразование. Возможно, компилятор просто пытается быть полезным, предоставляя вам больше перегрузок.

Учитывая вашу перегрузку и член ostream, разрешение перегрузки выходит из строя просто потому, что между ними нет правила приоритета (§13.3.1.2). Поэтому, поскольку перегрузка элемента char const * является единственной, с которой вы могли бы столкнуться, ваше исправление кажется подходящим.