Ошибка разрешения перегрузки при потоковой передаче объекта через неявное преобразование в строку - PullRequest
19 голосов
/ 13 июля 2011

Отказ от ответственности: Я знаю , что следует избегать неявного преобразования в строку и что правильным подходом будет перегрузка op<< для Person.


Рассмотрим следующий код:

#include <string>
#include <ostream>
#include <iostream>

struct NameType {
   operator std::string() { return "wobble"; }
};

struct Person {
   NameType name;
};

int main() {
   std::cout << std::string("bobble");
   std::cout << "wibble";

   Person p;
   std::cout << p.name;
}

Это дает следующее в GCC 4.3.4 :

prog.cpp: In function ‘int main()’:
prog.cpp:18: error: no match for ‘operator<<’ in ‘std::cout << p.Person::name’
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:112: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:121: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:131: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:169: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:173: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:177: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(bool) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:97: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:184: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(short unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:111: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:195: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:204: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:208: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long long unsigned int) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:213: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:217: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(float) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:225: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(long double) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/ostream:229: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(const void*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4/bits/ostream.tcc:125: note:                 std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]

Как получается свободный op<<(ostream&, string const&)не входит в набор перегрузки?Это из-за комбинации желаемой перегрузки, являющейся экземпляром шаблона и ... ADL?

Ответы [ 5 ]

18 голосов
/ 21 сентября 2011

14.8.1 / 4 в C ++ 98

Неявное преобразование (пункт 4) будет выполнено для аргумента функции, чтобы преобразовать его в тип соответствующего параметра функции, если тип параметра не содержит параметров-шаблона , которые участвуют в выводе аргумента шаблона.

Здесь вы хотели бы создать экземпляр

template <class charT, class traits, class Allocator>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>&,
               const basic_string<charT, traits, Allocator>&);

выводится без явного указания аргументов шаблона. Таким образом, все аргументы содержат шаблон-параметр , который участвует в выводе аргумента шаблона, и поэтому ни один из них не может получить свое значение из неявного преобразования.

7 голосов
/ 13 июля 2011

Это потому, что это шаблон.

Чтобы это работало, сначала вам нужно будет создать экземпляр шаблона, а затем использовать оператор преобразования.Это неправильный порядок, поэтому он не работает.


Не имеет значения, использовали ли вы ранее определенный оператор в программе или нет.Каждое использование рассматривается отдельно

Перегрузки, рассматриваемые в качестве кандидатов, - это те, в которых все параметры шаблона могут быть выведены из std :: ostream, или те, которые являются членами этого класса.


Что если мы добавим не шаблонный оператор?

#include <string> 
#include <ostream> 
#include <iostream>  

struct NameType {
   operator std::string() { return "wobble"; } 
};  

struct Person {
    NameType name;
};  

void operator<<(std::ostream& os, const std::string& s)   // ** added **
{ std::operator<<(os, s); }

int main() 
{    
    std::cout << std::string("bobble");
    std::cout << "wibble";

     Person p;
     std::cout << p.name; 
}  

Теперь он работает и выдает

 bobblewibblewobble
2 голосов
/ 13 июля 2011

Это потому, что пользовательская функция преобразования не учитывается в ADL.ADL означает, что набор перегрузки содержит функцию (и) перегрузки из пространства имен, в котором определен аргумент .Здесь тип аргумента operator<< равен NameType, но operator << (std::ostream&, const NameType&) не было определено в пространстве имен, в котором определен NameType.Отсюда и ошибка, так как поиск соответствующей перегрузки останавливается прямо там.Вот что такое ADL.ADL не идет дальше, чтобы посмотреть определение NameType, чтобы определить, определяет ли оно какую-либо пользовательскую функцию преобразования или нет.

Вы получите ту же ошибку , если вы это сделаетеследующее:

NameType name;
std::cout << name ; //error: user-defined conversion not considered.

Вам необходимо приведение it:

std::cout << (std::string)name << std::endl; //ok - use std::string()

Кроме того, у вас может быть несколько пользовательских функций преобразования:

std::cout << (int)name << std::endl; //ok - use int() instead

Вывод на ideone :

wobble
100
0 голосов
/ 23 сентября 2011

Преобразование в строку вызывается, только если в некоторых случаях:

a) явно запрашивается (string) p.name

b) присвоение строке string a = p.name

c)...

Если данный случай не подходит ни к одному, вы можете принудительно вызвать вызов ostream<<(ostream&,string) как минимум двумя способами:

  1. http://ideone.com/SJe5W Делая NameType быть строкой (путем публичного наследования).

  2. перейти к регистру a) : явный запрос преобразования, как видно изПример с преобразованием в (int).

Я действительно предпочитаю вариант 1 .

0 голосов
/ 13 июля 2011

Это потому, что определенные пользователем преобразования не могут быть связаны. Пояснить на примере:

struct A {
  void operator = (const int i);
};

struct B {
  operator int ();
}

A a;
B b;
a = b;  // error! because, compiler will not match "A::operator=" and "B::operator int"

Вот аналогичный вопрос , который я когда-то задал.

В вашем случае ваши первые определенные пользователем преобразования:

(1) NameType::operator std::string()

(2) operator <<(ostream&, const std::string&), что немного похоже на ostream::operator<<(std::string&).

Когда вы пишете, cout << p.name; Теперь два типа объектов сталкиваются лицом к лицу:

ostream (LHS) <====> NameType (RHS)

Теперь вызывается operator <<(ostream&, const string&) только , если RHS string. Но здесь это NameType; так что это не вызывается.

И, NameType::operator string () вызывается только , если LHS равен string. Но здесь это ostream; поэтому он не вызывается.

чтобы сделать это уравнение верным; Все перечисленные выше операторные методы должны вызываться компилятором. Но это не поддерживается C ++. Почему это не поддерживается, описано в ссылке, которую я разместил выше.

...