为什么在重载 >> 和 << 操作符时需要返回 istream/ostream 的引用?

18

如果我不返回dindout会发生什么,实际上我正在阅读一本书,作者在其中返回了流引用。

istream & operator>>(istream &din,vector &a)
{
    for(int i=0;i<size;i++)
    din>>a.v[i];
    return din;
}

ostream & operator<<(ostream &dout,vector &a)
{
    dout<<"("<<a.v[0];
    for(int i=1;i<size;i++)
    dout<<", "<<a.v[i];
    dout<<")";
    return dout;
}
4个回答

16
原因是几个事实的组合。
  1. You want to be able to chain input and output operations as in

    in  >> x >> y;
    
    out << z << std::precision(10) << t << std::endl;
    

    so you must return something that allows operator<< again.

  2. Since you want your operator to work on any istream, i.e. any object derived from std::istream, you cannot define

    operator<<(istream_type, object);    // take istream by value
    

    since this would only work for the specific istream type istream_type, but not for a generic istream. For that one must use polymorphism, i.e. either take a reference or a pointer (which will be a reference or pointer to a class derived from std::istream).

  3. Since you only have a reference to the istream, you cannot return the istream object itself (which may be of a type not even defined at the point of the definition of operator<<) but only the reference you've got.

    One could get around this restriction by defining operator<< a template and take and return the istream_type by value, but that requires the istream type to have a copy constructor, which it may well not have for good reasons.

  4. In order to envoke polymorphism one could, in principle, use pointers (to streams) rather than references. However, operator<<(stream*,const char*) is not allowed in C++ (at least one operand must be of class or enumeration type).

    Thus, with stream pointers one must use function-call syntax and you're back with C-style fprintf(stream*, args...).

    Moreover, pointers can be null or dangling, which in fact is their default state (when declared without initializer), while a reference can be assumed to be valid (it cannot be declared without initializer).


3
我不确定你关于指针的最后一个论点是否切中要点。如果这些运算符返回指针,我们将不得不像这样调用它们:*(*(out << foo) << bar) << baz; - 肯定没有人想要那种语法上的混乱。 - The Paramagnetic Croissant
@TheParamagneticCroissant 不是这样的。我编辑了答案。可以(使用)指针代替引用任何地方,包括标准库,它将像引用一样工作,除了空指针和/或悬挂指针的问题。 - Walter
1
指针的“默认状态”不是“NULL或悬挂”,而且很容易创建一个悬挂引用而不使用指针。在流函数中使用引用的原因是允许链接,而且使用引用还允许操作员假定对象存在(即调用者负责提供有效引用,而操作员不需要像使用指针一样检查有效性)。 - Rob
@Rob,我认为你最后提到的是我所说的空指针或悬挂指针。声明时没有初始化器的指针是空指针或悬挂指针。引用不能在没有初始化器的情况下声明。 - Walter
@Walter 如果您在参数中使用指针,则无法在rvalue流上调用这些运算符。我曾经看到过使用pre-C++11代码的情况,即 std::stringstream(the_string) >> the_int - The Paramagnetic Croissant
@TheParamagneticCroissant 是的,我们不能在 operator<< 中使用流指针。但是可以使用流指针来使用重载的 output(stream*, args..),这是 C 的做法。 - Walter

5
在这种情况下,当引用被返回时,您可以将运算符组合在一条链中。例如:
std::cout << "Hello " << "Rajat Verma";

这相当于调用以下运算符:
operator <<( operator <<( std::cout, "Hello" ), "Rajat Verma" );
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
              returns reference to std::cout 

3
更多的是ostream和istream标准对象,例如cout和cin使用私有副本构造函数,因此它们应该通过引用而不是值返回。

-1
当您输入以下代码时: cout << vector; cout 的类型为 ostream,所以当您使用 " << " 运算符时,它需要返回一个 ostream 类型的参数,以便 cout 能够正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接