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

此作品

#include 结构体{国际我;运算符 int() const noexcept {return i;}};int main() {国际我;i.i = 1;std::cout <<一世;}

但是,编译失败 G rel="1:noreferr.

#include #include <字符串>结构字符串{std::string s;运算符 std::string() const {return s;}};int main() {字符串 s;s.s = "你好";std::cout <<s;}

以下是错误的相关部分:

<块引用><块引用>

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

剪断

模板 std::basic_ostream<_CharT, _Traits>&std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::basic_string<_CharT, _Traits, _Alloc>&)
运算符<<(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?

解决方案

那个操作符是一个免费的 template 函数.当与 template 函数参数匹配时,不会检查用户定义的转换,而是使用类型模式匹配(替换).

理论上,使用 std::is_convertable<> 的 SFINAE 重载将能够做你想做的事,但是当 operator<< 时没有使用该技术将 std::string 输出到 basic_ostream 已定义.

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

我会这样做:

struct String {std::string s;运算符 std::string() const {return s;}朋友 std::ostream&运算符<<( std::ostream& os, String const& self) {返回 os<

这有一个额外的好处,即不会创建浪费的副本.

This works, printing 1:

#include <iostream>

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

int main() {
    Int i;
    i.i = 1;
    std::cout << i;
}

However, this fails to compile on 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;
}

Here are the relevant parts of the error:

error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream}’ and ‘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: note: template argument deduction/substitution failed:
main.cpp:25:18: note: ‘String’ is not derived from ‘const std::basic_string<_CharT, _Traits, _Alloc>’
std::cout << s;

I only use std::cout and std::string, which have the same template arguments. I'm really not sure why this wouldn't be able to pick up the implicit conversion like it did for Int. Why does it work with int, but not std::string?

解决方案

That operator is a free template function. User defined conversions do not get checked when matching against a template function arguments, it instead uses type pattern matching (substitution).

In theory a SFINAE overload using std::is_convertable<> would be able to do what you want, but that technique was not used when operator<< that outputs a std::string to a basic_ostream<char> was defined.

A manual overload to output your class to basic_ostream<...> will fix your problem.

I would do this:

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;
  }
};

which has the added benefit of not creating a wasted copy.

相关文章