重载运算符<<

时间:2011-03-02 18:07:35

标签: c++ oop class stream operator-overloading

我正在创建一个使用operator<<的简单类。它将存储两个并行的数据数组,每个数据类型具有不同(但已知)的数据类型。这个想法是最终的界面看起来像这样:

MyInstance << "First text" << 1 << "Second text" << 2 << "Third text" << 3;

这将使数组看起来像这样:

StringArray: | "First text" | "Second text" | "Third text" |
IntArray:    | 1            | 2             | 3            |

我可以处理检查输入的逻辑以确保一切都匹配,但我对operator<<的技术细节感到困惑。

我检查的教程说它将其重载为具有std::ostream&返回类型的友元函数,但我的类与流无关。我尝试使用void作为返回类型,但遇到了编译错误。最终我最终返回了对该课程的引用,但我不确定为什么会这样。

到目前为止,这是我的代码:

class MyClass
{
public:

MyClass& operator<<(std::string StringData)
{
    std::cout << "In string operator<< with " << StringData << "." << std::endl;

    return *this; // Why am I returning a reference to the class...?
}

MyClass& operator<<(int IntData)
{
    std::cout << "In int operator<< with " << IntData << "." << std::endl;

    return *this;
}
};

int main()
{   
MyClass MyInstance;
MyInstance << "First text" << 1 << "Second text" << 2 << "Third text" << 3;

return 0;
}

此外,我班级的用户可以做这样的事情,这是不需要的:

MyInstance << "First text" << 1 << 2 << "Second text" << "Third text" << 3;

我可以做些什么来强制输入的交替性质?

3 个答案:

答案 0 :(得分:7)

ostream运算符返回对ostream的引用的原因,以及它有助于您的案例返回对MyClass的引用的原因是A << B << C这样的表达式总是被解释为(A << B) << C。也就是说,无论第一个重载操作符返回什么,都会成为下一个操作符调用的左侧。

现在,如果您希望像MyInstance << "First text" << 1 << 2这样的表达式产生编译器错误,您需要确保在前两个<<运算符之后返回的类型是一个无法用另一个运算符调用的类型INT。我认为这样的事情可能会做你想要的(感谢@Pete Kirkham的改进主意):

struct MyClass_ExpectInt;
class MyClass {
private:
    friend MyClass& operator<<(const MyClass_ExpectInt&, int);
    void insert_data(const std::string& StringData, int IntData);
    // ...
};
struct MyClass_ExpectInt {
    MyClass& obj_ref;
    std::string str_data;
    explicit MyClass_ExpectInt( MyClass& obj, const std::string& str )
      : obj_ref( obj ), str_data( str ) {}
};
MyClass_ExpectInt operator<<( MyClass& obj, const std::string& StringData )
{
    // Do nothing until we have both a string and an int...
    return MyClass_ExpectInt( obj, StringData );
}
MyClass& operator<<( const MyClass_ExpectInt& helper, int IntData )
{
    helper.obj_ref.insert_data( helper.str_data, IntData );
    return helper.obj_ref;
}

这些是您定义的与operator<<相关的唯一两个重载MyClass函数。这样,每次调用operator<<时,编译器都会将返回类型从MyClass&切换到MyClass_ExpectInt,反之亦然,并将“错误”类型的数据传递给{{1绝不允许。

答案 1 :(得分:2)

MyInstance << "First text" << 1;

此行调用operator<<(operator<<(MyInstance, "First text"), 1)。如果operator<<未返回对MyClass的引用,则“外部”调用将失败,因为您将在预期MyClass &的位置传递void。

为了强制执行交替类型的编译时,你需要创建一个帮助类,例如MyClassHelper。然后,您需要创建这两个运算符:

MyClassHelper & operator<<(MyClass &, std::string const &);
MyClass & operator<<(MyClassHelper &, int);

然后,每个运算符都应该返回对“其他”所涉及对象的引用。这确保在<< "string"之后返回的引用是MyClassHelper,它只有一个运算符&lt;&lt;对于int。在<< int之后,返回的引用是一个MyClass,它只有一个运算符&lt;&lt;用于字符串

答案 2 :(得分:0)

除了schepler所说的内容:语法

x << "one" << 1 << "two" << 2;

没有表明“一个”属于1.今天看起来很好,但明天很难向其他人解释,而且两年内维护人员更难对其进行逆向工程。那是因为它类似于'常规'插入操作符, 支持更多类型。

如果您可以自由选择API的外观,那么现在最好先做一些事情,以便更清楚地知道您真正插入了两个相关值。

这可以通过例如仅允许插入std::pair<string,int>

x << make_pair( "one", 1 ) 
  << make_pair( "two", 2 )
  ;