通过将string隐式转换为对象时,重载parsing失败

免责声明:知道应该避免隐式转换为string,并且适当的方法将是对Personop<<


考虑下面的代码:

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

为什么free op<<(ostream&, string const&)没有进入重载集? 这是由于所需的重载模板实例化和ADL的组合吗?

C ++ 98中的14.8.1 / 4

如果参数types不包含参与模板参数推导的模板参数 ,则将对函数参数执行隐式转换(第4节),以将其转换为相应函数参数的types。

在这里你想要一个实例化

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

在不明确提供任何模板参数的情况下被推导出来。 因此,所有的参数都包含一个参与模板参数演绎的模板参数,因此它们都不能从隐式转换中获得它的值。

这是因为它是一个模板。

为此,您需要首先实例化模板,然后使用转换操作符。 这是错误的顺序,所以它不起作用。


如果您在程序的早期使用了特定的操作符,则无关紧要。 每个用途都被分开考虑

被认为是候选者的重载是所有的模板参数都可以从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 

它是由于ADL中不考虑用户定义的转换函数。 ADL表示重载集包含来自定义参数的名称空间的重载函数。 这里, operator<<的参数typesNameTypeoperator << (std::ostream&, const NameType&)尚未在名称空间中定义。 因此,寻找合适的过载的错误在那里停止 。 这是ADL的。 ADL不会进一步研究NameType的定义来确定它是否定义了任何用户定义的转换函数。

你会得到同样的错误,如果你做到以下几点:

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

你需要投它:

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

另外,您可能有多个用户定义的转换函数:

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

ideone的输出:

 wobble 100 

只有在某些情况下才会调用string转换:

a)显式请求(string) p.name

b)赋值给string a = p.name

C) …

如果目前的情况不适合,您可以至less用两种方法强制调用ostream<<(ostream&,string)

  1. http://ideone.com/SJe5W使NameType 成为一个string(通过公共inheritance)。

  2. 去例a) :显式地请求转换,如在例子中看到的,转换为(int)

我真的更喜欢选项1

这是因为用户定义的转换不能被链接。 用一个例子来解释一下:

 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; 现在两种types的物体面对面:

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

现在, 只有当RHS是stringoperator <<(ostream&, const string&) 才被调用。 但是这里是NameType ; 所以没有被调用。

而且, 当LHS是string时, 调用NameType::operator string () 。 但在这里,它是ostream ; 所以没有被调用。

为了使这个方程成立; bot的上述操作符方法应该由编译器调用。 但是这不被C ++支持。 为什么不支持,在我上面发布的链接中描述。