提问人:user12002570 提问时间:3/18/2022 最后编辑:user12002570 更新时间:8/4/2022 访问量:180
我们可以重载运算符吗<<第一个参数的类型是 std::ostream&& 而不是 std::ostream&
Can we overload operator<< with the first parameter being of the type std::ostream&& instead of std::ostream&
问:
我了解到我们可以超载,如下所示:operator<<
class Person
{
public:
friend std::ostream& operator<<(std::ostream& os, const Person& obj);
};
我完全理解引用参数类型的原因。例如,第一个参数是引用,因为无法复制流,第二个参数是引用,因为我们希望反映对原始对象所做的更改(如果有的话)。我知道,由于我们在第二个参数中有一个低级常量,因此无法更改其状态,并且通过使用引用,我们可以避免复制。operator>>
我的问题是,我们是否可以(并且应该)对第一个参数使用右值引用而不是左值引用,如下所示:
friend std::ostream& operator<<(std::ostream&& os, const Person& obj); //note the first parameter is rvalue-reference
我们有什么理由应该/不应该做上面显示的事情吗?更重要的是,如果我们这样做会发生什么。
同样,我的第二个问题是,我们是否可以将返回类型设置为 而不是 .在这种情况下,会发生什么/变化。std::ostream&&
std::ostream&
PS:我正在学习C++,出于好奇问了这个问题。也就是说,加深我对引用和重载的了解。
答:
没有技术原因可以解释为什么你不能做其中任何一个。但是,如果这样做,则不会与定义运算符的所有其他内容进行互操作。ostream
如果输入是,则不能在左侧有左值流ostream &&
Person p;
std::cout << p // can't bind std::cout to ostream&&
将右值引用返回到通过左值引用接收的内容是不礼貌的。
std::ostream&& operator<<(std::ostream& os, const Person& obj) {
// something involving obj
return std::move(os); // stealing something that shouldn't be stolen!
}
如果你想有一个带有临时表达式的表达式,你可以。标准库定义了一个模板,该模板采用右值流,应用等效的左值流操作,并移动返回该流。ostream
评论
std::fstream("person.txt") << p
std::ostream &
fstream
如果您尝试编写如下内容,则程序将无法编译(@PaulMcKenzie的评论中已经提到了这一点):
#include <iostream>
#include <ostream>
struct Person {
int x = 0;
};
std::ostream& operator<<(std::ostream&& os, const Person& obj)
{
return os << obj.x;
}
int main()
{
Person p;
std::cout << p;
}
但是,您可以通过将最后一行更改为main
std::move(std::cout) << p; // don't do this in real code
然后,程序将编译并输出 .但是,在此之后,流将处于“已移出”状态,不应再使用。0
std::cout
关于一些评论,似乎对什么是 r 值引用存在误解。有些人似乎认为它只是一个更好的 l 值参考,我们在 C++11 之前无法访问。
L 值引用是指可以在表达式左侧使用的内容。您可以将其视为对实际对象的引用,可以更改并分配给该对象,依此类推。另一方面,r 值引用是指可以在赋值右侧使用的临时对象,即某物。
假设您有两个具有以下签名的重载。operator<<
std::ostream& operator<<(std::ostream& os, const Person& obj);
std::ostream& operator<<(std::ostream&& os, const Person& obj);
规则是,第一个接受 l 值或 r 值引用。后者仅接受 r 值引用。如果我们用 l 值调用运算符( 就是一个例子),将使用第一个重载。如果我们用 r 值(如 )调用运算符,则将使用第二个值。您可以省略 r 值引用重载,在这种情况下,始终选择第一个重载,但如果要调用具有 l 值的运算符,则不能省略第一个重载。但是,使用 l 值调用运算符是您几乎总是想要的:std::cout
std::move(std::cout)
std::cout << p; // will not compile if there is only an r-value reference overload
现在,r值引用的用例是什么?它们用于在对象上实现移动语义。R 值引用用于引用对象,这些对象在当前语句之后不再有效(它们没有持久性)。虽然理论上您可以移动流,就像上面所示,但这根本没有用。流旨在用作具有较长生存时间的对象,而不是临时对象。std::cout
所以最后回答你的问题:你不应该这样做,因为流通常不用作临时对象。如果您真的想为算子提供 r 值参考重载,请继续,但也要为 的正常用例提供 l 值重载。<<
std::cout << p;
评论
std::cout
在您的示例中未移动。你必须搬到一个不同的地方才能打破一切。os
ostream
std::move
我的问题是,我们是否可以(并且应该)对第一个参数使用右值引用而不是左值引用,如下所示:
friend std::ostream& operator<<(std::ostream&& os, const Person& obj);注意:第一个参数是 rvalue-reference
这在技术上是可行的(有一些限制),但绝对是非常奇怪的事情。使用右值引用通常意味着对象将被“消耗”(例如,从中移动),并且不应在后续操作中使用。但是,通常写入流不会消耗它;该流仍可用于后续写入。
评论
cout << person;