问题描述
请考虑以下struct
与用户定义的转换功能,该功能可以将自身转换为const char*
;
struct S {
operator const char*() { return "hello"; }
};
与<iostream>
一起使用,我们可以打印struct S
而不会出现错误消息:
std::cout << S{} << '\n';
但是,如果我将返回类型更改为std::string
:
struct S {
operator std::string() { return "hello"; }
};
我收到此编译器错误消息:
<source>:11:13: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'S')
11 | std::cout << S{} << '\n';
| ~~~~~~~~~ ^~ ~~~
| | |
| | S
| std::ostream {aka std::basic_ostream<char>}
<source>:11:18: note: 'S' is not derived from 'const std::__cxx11::basic_string<_CharT,_Traits,_Allocator>'
11 | std::cout << S{} << '\n';
| ^
为什么编译器不能使用std::string
转换?内置类型和类类型的转换函数之间有区别吗?
解决方法
因为operator<<
for std::basic_string
是一个采用3个模板参数的模板:
template <class CharT,class Traits,class Allocator> std::basic_ostream<CharT,Traits>& operator<<(std::basic_ostream<CharT,Traits>& os,const std::basic_string<CharT,Traits,Allocator>& str);
在template argument deduction中将不考虑隐式转换:
类型推导不考虑隐式转换(上面列出的类型调整除外):这是超载解析的工作,稍后会发生。
那么给定的std::cout << S{};
,模板参数CharT
,Traits
和Allocator
不能在第二个函数参数上推导。
另一方面,operator<<
for const char*
没有这样的问题;给定std::cout << S{};
,模板参数CharT
和Traits
仅从第一个函数参数推导出。扣除后,将执行从S
到const char*
的隐式转换,并且调用效果很好。