C++中,如果两个对象都重载了运算符operator<<,那么a<<b会发生什么?

4

I thought that in

cout << "Hello world" 

cout对象有一个运算符重载,因此我们可以将字符串传递到cout对象的成员函数中。

但是,在一些示例代码中,我看到一个类中定义了运算符重载。

class GenericPlayer : public Hand
{
    ..
    friend ostream& operator <<(ostream& os, const GenericPlayer& aGenericPlayer);
    ..
};

...
cout << aGenericPlayer << endl;
...

即使不是这样,如果coutaGenericPlayer都重载了operator<<,会怎么样呢?

2
你是在问是否coutGenericPlayer都为GenericPlayer定义了插入运算符(<<)? - GManNickG
3
如果有多个operator<<的定义与给定的操作数匹配,那么根据C++03 §13.3 [over.match]中详细解释的复杂重载决议规则会选择最佳匹配。如果“最佳”匹配不唯一或不存在,则会出现错误。 - Adam Rosenfield
4个回答

8
即使不是这种情况,如果cout和aGenericPlayer都重载了operator<<会怎么样?
std::cout是一个std::ostream对象,因此任何std::ostream& operator<<(std::ostream, SomeType)都可以与std::cout一起使用。但关键在于操作符的第二个参数是不同的,因此重载是不同的。第一个“字符串”重载大概是这样的
std::ostream& operator<<(std::ostream&, const char*);

还有第二个

std::ostream& operator <<(std::ostream& os, const GenericPlayer& aGenericPlayer); 

因此,它们是不同的运算符重载,不存在歧义。


我想你的意思是“所以operator<<(std::ostream&,something_else)可以与cout一起使用”? - Reuben Morais
@ReubenMorais 是的,那正是我所想的。已编辑。 - juanchopanza

2

首先,coutaGenericPlayer都不能重载任何东西。它们是对象,而重载是基于类型的(即使通常不会说类型X重载了<<,而应该说有一个可以将类型X作为其第二个参数的重载<<)。

其次,重载决议是基于所有参数的,而不仅仅是一个参数。在std::istream中有大约20种不同的<<重载方式(它是std::cout类型的基类),但是没有一种(至少在标准库中)以GenericPlayer作为第二个参数。因此,如果第二个操作数不是GenericPlayer,则无法使用它们。同样,除了现有的operator<<(int, GenericPlayer const&)之外,您还可以添加一个;如果左侧的类型为int,右侧的类型为GenericPlayer,则会调用它。(我想不到任何情况下这不是运算符重载滥用,但语言肯定允许它。)


1
为了让 cout 接受一个 GenericPlayer 对象,你必须重载 operator<<operator<< 也被称为插入运算符。因此,如果你将其放在上下文中,你正在将自定义函数的输出插入到 cout 中。重载的运算符返回对原始 ostream 对象的引用,这也意味着你可以组合插入。你必须重载插入运算符以识别左侧的 ostream 对象和右侧的 GenericPlayer。参见 Overloading the << Operator for Your Own Classes。就 cout 而言,cout 是代表标准输出流的 class ostream 对象。它对应于 cstdiostdout。因为 cout 是 class ostream 的对象,我们既可以使用插入运算符(ostream::operator<<)以格式化数据的形式向其中写入字符,也可以使用 write 成员函数等方式以非格式化数据的形式写入。

我实际上同意你的基本想法,即 << 运算符是插入运算符。但在像 BigInteger operator<<( BigInteger const& lhs, int rhs ) 这样的情况下,是否可以使用这个术语?从人类读者的角度来看,运算符 << 被重载为插入或左移。 - James Kanze
@JamesKanze:当应用于 ostream 运算符时,我相信惯例是“插入”。 - user195488
1
同意。对于大多数应用程序来说,这是唯一的用途;大多数程序员可能认为插入是运算符的主要含义(尽管从历史上看,它首先是左移运算符)。 - James Kanze
@JamesKanze:您可能会对这个SO问题感兴趣:https://dev59.com/22445IYBdhLWcg3wcZ8N - user195488

1
如果cout和aGenericPlayer都重载了运算符“<<”呢?
他们两个都没有重载,它是作为一个普通函数(不是成员函数)被重载的。请注意您的示例中使用的“friend”,这允许该函数访问类的内部而不是成员。因此,避免了这种情况的发生。

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