为什么输出带转换运算符的类不适用于std :: string?

chr*_*ris 5 c++ templates compiler-errors implicit-conversion

这个工作,打印1:

#include <iostream>

struct Int {
    int i;
    operator int() const noexcept {return i;}
};

int main() {
    Int i;
    i.i = 1;
    std::cout << i;
}
Run Code Online (Sandbox Code Playgroud)

但是,这无法在GCC 4.8.1上编译:

#include <iostream>
#include <string>

struct String {
    std::string s;
    operator std::string() const {return s;}
};

int main() {
    String s;
    s.s = "hi";
    std::cout << s;
}
Run Code Online (Sandbox Code Playgroud)

以下是错误的相关部分:

错误:'operator <<'不匹配(操作数类型是'std :: ostream {aka std :: basic_ostream}'和'String')
std :: cout << s;

SNIP

template std :: basic_ostream <_CharT,_Traits>&std :: operator <<(std :: basic_ostream <_CharT,_Traits>&,const std :: basic_string <_CharT,_Traits,_Alloc>&)
operator <<(basic_ostream <_CharT ,_Traits>&__os,

/usr/include/c++/4.8/bits/basic_string.h:2753:5:注意:模板参数推断/替换失败:
main.cpp:25:18:注意:'String'不是从'const std ::派生的basic_string <_CharT,_Traits,_Alloc>'
std :: cout << s;

我只使用std::coutstd::string,它们具有相同的模板参数.我真的不确定为什么它不能像它那样获取隐式转换Int.为什么它可以使用int,但不是std::string

Yak*_*ont 7

该运算符是一个自由template函数.在匹配template函数参数时,不会检查用户定义的转换,而是使用类型模式匹配(替换).

理论上,SFINAE过载使用std::is_convertable<>可以做你想要的,但是当定义operator<<输出a std::string到a 时,没有使用该技术basic_ostream<char>.

输出类的手动超载basic_ostream<...>将解决您的问题.

我会这样做:

struct String {
  std::string s;
  operator std::string() const {return s;}
  friend std::ostream& operator<<( std::ostream& os, String const& self) {
    return os<<self.s;
  }
};
Run Code Online (Sandbox Code Playgroud)

它具有不创建浪费副本的额外好处.