Это на самом деле интересный вопрос.Основная проблема, как уже указывали другие, заключается в том, что вы объявили следующую подпись:
template <typename T>
std::ostream& operator<<( std::ostream&, T const & );
И это вызывает неоднозначность, поскольку это шаблон catch-all .Но почему компилятор может (однозначно) вставить целое число в cout
, но не может вставить const char*
?
Причина этого заключается в определении шаблона std::basic_ostream
и свободных функцийчто требуется в стандарте.В частности, шаблон класса basic_ostream
содержит member функции для вставки основных типов, включая int
.С другой стороны, вставка const char*
в потоки определяется как шаблонная свободная функция.Собираем вместе три объявления:
namespace std {
template <typename CharT, typename traits = char_traits<CharT> >
class basic_ostream {
// ...
basic_ostream<CharT,traits>& operator<<(int n); // [1]
// ...
};
template<class charT, class traits> // [2]
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const char*);
}
template <typename T> // [3]
std::ostream& operator<<( std::ostream&, T const & ); // user defined
Теперь, когда компилятор встречает выражение std::cout << 5
, он обнаруживает, что [1] является идеальным соответствием без шаблонов.Он не шаблонизирован, поскольку std::cout
является объектом конкретного создания шаблона класса basic_ostream
, когда компилятор рассматривает члены этого класса, тип является фиксированным.Сам метод не является шаблонным.
Шаблон [3] мог бы соответствовать тому же использованию, но поскольку [1] не шаблонизирован, он имеет приоритет в разрешении перегрузки, и нет никакой неоднозначности.
Теперь, когда компилятор видит выражение std::cout << "Hello world";
, он выполняет поиск и находит (среди других опций, которые не могут быть сопоставлены и, следовательно, отбрасываются), опции [2] и [3].Проблема состоит в том, что теперь оба параметра являются шаблонами, первый из них можно разрешить путем сопоставления CharT = char
и traits = char_traits<char>
, а второй можно сопоставить, введя T = const char*
(первый аргумент - конкретный экземплярный тип).Компилятор не может определиться (нет частичного порядка, который определяет, какой опции он должен следовать), и он вызывает ошибку неоднозначности.
Действительно интересный момент в этом вопросе заключается в том, что как [1], так и[2] кажется, что они основаны на аргументах CharT
и traits
в основном одинаково, поскольку компилятор не учитывает их одинаково, причина в том, что поиск находит [1] как член std::cout
, это означает, что в [1] basic_ostream<char,char_traits<char> >
является конкретным известным типом первого аргумента, и он фиксирован.Шаблон - это класс, а не функция, и типы создания экземпляров класса фиксируются до того, как поиск учитывает функции-члены.С другой стороны, когда ADL находит [2] и пытается сопоставить вызов, basic_ostream<CharT, traits>
является универсальным типом , который можно сопоставить с типом cout
.
* 1043.* Я надеюсь, что это не слишком запутанно, но я думаю, что приятно знать тонкую разницу в
аналогично выглядящем коде.