首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我们能不能用第一个参数std::of来重载operator<< &&而不是std::ostream&

我们能不能用第一个参数std::of来重载operator<< &&而不是std::ostream&
EN

Stack Overflow用户
提问于 2022-03-18 12:33:19
回答 3查看 116关注 0票数 0

我了解到我们可以重载operator<<,如下所示:

代码语言:javascript
复制
class Person 
{
    public:
        friend std::ostream& operator<<(std::ostream& os, const Person& obj);
};

我绝对理解参数类型被引用的原因。例如,第一个参数是引用,因为不能复制流,而第二个参数是引用,因为我们希望反映对原始对象所做的更改(如果有operator>>的话)。我知道,由于我们在第二个参数中有一个低级const,它的状态不能改变,通过使用引用,我们避免了复制。

我的问题是,对于第一个参数,我们可以(也应该)使用rvalue引用而不是lvalue引用,如下所示:

代码语言:javascript
复制
friend std::ostream& operator<<(std::ostream&& os, const Person& obj); //note the first parameter is rvalue-reference

我们是否有理由不做上面所示的事情?更重要的是,如果我们这样做会发生什么。

同样,我的第二个问题是,我们是否可以将返回类型改为std::ostream&&而不是std::ostream&。在这种情况下,会发生什么/发生什么变化。

PS:我正在学习C++,出于好奇我问了这个问题。那就是,加深我对参考文献和超载的认识。

EN

回答 3

Stack Overflow用户

发布于 2022-03-18 14:06:08

如果尝试编写如下内容,程序将不会编译(@PaulMcKenzie的注释中已经提到了这一点):

代码语言:javascript
复制
#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中的最后一行更改为

代码语言:javascript
复制
std::move(std::cout) << p; // don't do this in real code

然后,该程序将编译并输出0。然而,在此之后,std::cout流将处于移出状态,不应该再使用了。

对于其中的一些评论,似乎对r值引用是什么有误解。有些人似乎认为这只是一个更好的l值引用,我们在C++11之前无法访问它。

L-值引用指的是可以在表达式左手边使用的东西.您可以将其看作是对实际对象的引用,该对象可以更改并分配给等等。另一方面,r值引用是指一个临时对象,某个东西,可以在任务的右侧使用。

假设您有两个带有以下签名的operator<<重载。

代码语言:javascript
复制
std::ostream& operator<<(std::ostream& os, const Person& obj);
std::ostream& operator<<(std::ostream&& os, const Person& obj);

规则是,第一个接受l值或r值引用.后者只接受r值引用。如果我们用l-值调用操作符(std::cout就是这方面的一个例子),那么将使用第一个重载。如果我们用r值(如std::move(std::cout))调用操作符,则使用第二个操作符。您可以省略r值引用重载,在任何情况下都会选择第一个重载,但是如果要使用l值调用操作符,则不能忽略第一个重载。但是,使用l值调用运算符是您几乎总是想要的:

代码语言:javascript
复制
std::cout << p;  // will not compile if there is only an r-value reference overload

那么r值引用的用例是什么呢?它们用于实现对象上的移动语义。R值引用用于引用对象,这些对象在当前语句之后不再有效(它们没有持久性)。虽然理论上您可以移动std::cout流,就像上面所示的那样,但这一点也没有用。流是指用作具有长生存期的对象,而不是临时对象。

最后,回答您的问题:您不应该这样做,因为流通常不被用作临时对象。如果您真的想向<<操作符提供r值引用重载,那么请继续,但是也为std::cout << p;的正常用例提供了一个l值重载。

票数 0
EN

Stack Overflow用户

发布于 2022-08-04 13:40:34

我的问题是,对于第一个参数,我们可以(也应该)使用rvalue引用而不是lvalue引用,如下所示: 朋友std::ostream& operator<<(std::ostream&& os,const & obj);//注意第一个参数是rvalue-引用

这在技术上是可能的(有一些限制),但肯定是非常奇怪的事情。使用rvalue引用通常意味着对象将被“消耗”(例如,从移出),并且不应该在以后的操作中使用。但是,一般情况下,写入流并不消耗它;流仍然可以用于后续的写入。

票数 0
EN

Stack Overflow用户

发布于 2022-03-18 14:02:59

没有任何技术上的原因可以解释为什么你不能这样做。但是,如果您这样做了,则不会与定义ostream运算符的所有其他东西进行互操作。

如果输入是ostream &&,那么就不能在左边有一个lvalue流

代码语言:javascript
复制
Person p;
std::cout << p // can't bind std::cout to ostream&&

将rvalue引用返回给您通过lvalue引用接收到的内容是不礼貌的。

代码语言:javascript
复制
std::ostream&& operator<<(std::ostream& os, const Person& obj) {
    // something involving obj
    return std::move(os); // stealing something that shouldn't be stolen!
}

如果希望有一个带有临时ostream的表达式,则可以。标准库定义模板接受rvalue流,应用等效的lvalue流操作,并移动-返回该流。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71527081

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档