将 std 字符串访问器与 ostream 运算符<<一起使用

Using std string accessor with ostream operator <<

提问人:user2871308 提问时间:11/15/2022 最后编辑:user2871308 更新时间:11/15/2022 访问量:143

问:

如果我创建一个类:

// First Example
#include <iostream>
#include <string>

class my_class {
  std::string str;
public:
  my_class(const char* s = "") : str(s) {}

  operator const char* () const { return str.data(); } // accessor
};

my_class mc1{"abc"};
std::cout << mc1; // Calls the char* accessor and successfully writes "abc" to screen output.

如果我这样修改类:

// Second Example
class my_class {
  std::string str;
public:
  my_class(const char* s = "") : str(s) {}

  operator std::string () const { return str; } // accessor
};

my_class mc1{"abc"};
std::string mystring = mc1; // Calls the string accessor
std::cout << mystring; // Also successfully writes "abc" to screen output.

但是,如果我尝试调用:

std::cout << mc1;

我会得到一个充满编译错误的页面,这些错误以以下开头:

错误 C2679:二进制“<<”:未找到采用类型为“my_class”的右手操作数的运算符(或没有可接受的转换)

我可以通过添加到第二个示例类来纠正此错误:

  friend std::ostream& operator <<(std::ostream& os, my_class& rhs) {
    os << rhs.str;
    return os;
  }

我主要是从这个问题的建议解决方案之一中摘录的。但我不明白为什么需要使用字符串访问器而不是 char* 访问器。

我期望成功编译和输出 mc1.str 的值,或者我本来希望在第一个示例中使用 char* 访问器函数时遇到同样的错误。相反,我只在第二个示例中收到了 C2679。

更新:我看到在 ostream 中使用强制转换运算符,例如 ,将显式调用字符串访问器并将字符串写入屏幕。std::cout << (std::string)mc1;

C++ 字符串 重载 IOSTREAM 访问器

评论

0赞 ChrisSc 11/15/2022
该错误准确地描述了问题。流类没有从它理解的东西的转换;Stream 类不知道该怎么处理 。换句话说,流类还没有被编程(显然)。通过提供友元实现,您明确提供了所需的转换。friend 函数被调用,因为它的参数与语句匹配my_classmy_classmy_class
0赞 user2871308 11/15/2022
感谢 @Scheff'sCat 发现我的复制错误。

答:

6赞 NathanOliver 11/15/2022 #1

发生这种情况是因为函数的定义方式。

对于这种情况,可用于该情况的声明为:const char*operator <<cout

template< class CharT, class Traits >
basic_ostream<CharT, Traits>&
    operator<<( basic_ostream<CharT, Traits>& os, const char* s );

因此,当编译器分析时,它可以推断出 what 和 are from ,并发现将其转换为 ,因此重载解析成功,代码可以编译。std::cout << mc1;CharTTraitscoutmy_class::operator const char* ()mc1const char*

当您切换到 having and use 时,您现在需要调用 的重载,该重载声明为:operator std::string ()std::cout << mc1;std::stringoperator <<

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

在此重载中,不仅第一个参数依赖于模板参数,第二个参数也依赖于模板参数。这意味着编译器将尝试直接推导出 的类型。在此步骤中不考虑任何转换运算符,并且由于实际上不是 ,因此推导失败,并且没有可能的重载,因此代码无法编译。CharTTraitsAllocatormc1mc1std::string

评论

0赞 user2871308 11/15/2022
“在此步骤中不考虑转换运算符 [模板解析?]”这只是一个规则吗?我很欣赏basic_string是一个模板,但我认为 typedef 字符串已经解析得足够多,以至于隐式转换可以调用访问器。是编译器看到my_class类型并首先将其分配给 CharT,然后再确定这实际上不适用于写入 ostream 的目的吗?试图理解一般原理。
2赞 NathanOliver 11/15/2022
@user2871308 是 的句法糖。在模板参数推导期间,编译器会分析传递给函数的参数类型,并尝试从中推导出模板参数。 不是 ,所以它无法推导任何参数,因此完整的推导过程失败,你没有能够调用的函数。是编译器看到my_class类型并首先将其分配给 CharT,然后再确定这实际上不适用于写入 ostream 的目的吗?几乎。std::cout << mc1;operator <<(cout, mc1)mc1std::string