通过将string隐式转换为对象时,重载parsing失败
免责声明:我知道应该避免隐式转换为string,并且适当的方法将是对Person
的op<<
。
考虑下面的代码:
#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<<
的参数types是NameType
但operator << (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)
:
-
http://ideone.com/SJe5W使NameType 成为一个string(通过公共inheritance)。
-
去例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是string
, operator <<(ostream&, const string&)
才被调用。 但是这里是NameType
; 所以没有被调用。
而且, 仅当LHS是string
时, 才调用NameType::operator string ()
。 但在这里,它是ostream
; 所以没有被调用。
为了使这个方程成立; bot的上述操作符方法应该由编译器调用。 但是这不被C ++支持。 为什么不支持,在我上面发布的链接中描述。