通过隐式转换为字符串流式传输对象时,重载决策失败

时间:2011-07-13 09:52:51

标签: c++ operator-overloading std implicit-conversion

免责声明:知道应避免隐式转换为字符串,并且正确的方法是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;
}

yields the following on 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 个答案:

答案 0 :(得分:18)

14.8.1 / 4 in C ++ 98

  

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

在这里,您希望实例化

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

在不明确提供任何模板参数的情况下推断出来。因此,所有参数都包含参与模板参数推导的 template-parameter ,因此它们都不能从隐式转换中获取其值。

答案 1 :(得分:7)

这是因为它是一个模板。

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


如果您之前在程序中使用过特定的操作符,则无关紧要。每次使用都单独考虑

作为候选者的重载是那些可以从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 :(得分:2)

因为在ADL中不考虑用户定义的转换函数。 ADL表示重载集包含来自定义参数的命名空间的重载函数。这里operator<<的参数的类型NameType,但operator << (std::ostream&, const NameType&)尚未在定义NameType的命名空间中定义。因此,错误,因为在那里搜索适当的重载停止。这就是ADL。 ADL没有进一步研究NameType的定义,以确定它是否定义了任何用户定义的转换函数。

如果您执行以下操作,您将获得the same error

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

您需要cast

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

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

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

ideone的输出:

wobble
100

答案 3 :(得分:0)

只有在某些情况下才会调用转换为字符串:

a)明确要求(string) p.name

b)分配给字符串string a = p.name

c)......

如果本案不符合任何规定,您可以通过至少两种方式强制调用ostream<<(ostream&,string)

  1. http://ideone.com/SJe5W使NameType 成为字符串(通过公共继承)。

  2. 转到案例 a):明确请求转化,如转换为(int)的示例所示。

  3. 我真的更喜欢选项 1

答案 4 :(得分:-3)

那是因为用户定义的转换无法链接。用一个例子来解释:

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"

这是similar question,我有时会回答。

在您的情况下,您的第一个用户定义的转化次数为

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

(2)operator <<(ostream&, const std::string&)有点像ostream::operator<<(std::string&)

当你写作时,cout << p.name;现在有两种类型的对象面对面:

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

现在,如果RHS为operator <<(ostream&, const string&),则string仅会调用 。但这里是NameType;所以它没有被调用。

如果LHS为NameType::operator string (),则string仅会被调用。但这里是ostream;所以它没有被调用。

使这个等式成立; bot应该由编译器调用上述运算符方法。但是C ++不支持。为什么它不受支持,在我上面发布的链接中有描述。