Ответ 1
Вспомним, что std::string
не является автономным типом, он действительно специализируется на шаблоне шаблона - std::basic_string<char>
. Очень важная деталь заключается в том, что потенциальная перегрузка для потоковой передачи a std::string
не принимает аргумент std::string const&
, это шаблон функции, который выводит a std::basic_string const&
:
template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::basic_string<CharT, Traits, Allocator>& str);
Вычисление шаблонов никогда не учитывает конверсии. Поиск имени найдет этот шаблон функции, а затем отменит его как нежизнеспособный из-за отказа дедукции. S
не является basic_string<CharT, Traits, Allocator>
для любых таких типов, поэтому мы закончили. Единственными жизнеспособными операторами потока будут все интегральные, из которых bool
- наилучшее совпадение.
Если была определенная функция с сигнатурой:
std::ostream& operator<<(std::ostream&, std::string const& );
Тогда вызов будет неоднозначным - вы получите две пользовательские конверсии, которые будут одинаково ранжированы.
Это легко проверить, используя наши собственные функции вместо миллионов перегрузок для operator<<
:
void foo(bool ); // #1
void foo(std::string ); // #2
void bar(bool ); // #3
template <class C, class T, class A>
void bar(std::basic_string<C,T,A> ); // #4
foo(S{}); // error: ambiguous
bar(S{}); // calls #3