通过隐式转换为字符串对对象进行流式处理时,重载解析失败

Overload resolution failure when streaming object via implicit conversion to string

本文关键字:处理 失败 重载 转换 字符串 对象      更新时间:2023-10-16

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

为什么空闲的op<<(ostream&, string const&)没有进入过载集?这是因为所需的重载是模板实例化和。。。ADL?

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>&);

可以在不显式提供任何模板参数的情况下推导。因此,所有参数都包含一个参与模板参数推导的模板参数,因此没有一个参数可以从隐式转换中获得其值。

这是因为它是一个模板。

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


在程序的早期是否使用了特定的运算符并不重要。每次使用都单独考虑

被认为是候选者的重载是那些所有模板参数都可以从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<<的参数的类型NameType,但operator << (std::ostream&, const NameType&)尚未在定义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

视频输出:

wobble
100

只有在以下情况下才会调用到字符串的转换:

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

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

c( 。。。

如果目前的情况不适合任何情况,您可以通过至少两种方式强制调用ostream<<(ostream&,string)

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

  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&),有点像CCD_。

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

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

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

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

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