内置赋值运算符的返回类型是什么?

What is the return type of the built-in assignment operator?

提问人:oczkoisse 提问时间:3/8/2013 最后编辑:Jan Schultkeoczkoisse 更新时间:10/3/2023 访问量:50688

问:

我刚刚开始C++。我对赋值和取消引用运算符的返回类型有点困惑。我正在阅读《C++入门》一书。在各种场合,作者说赋值运算符的返回类型是指左手操作数的类型,但后来,他说返回类型是左手操作数的类型。我参考了C++11标准第5.17节,其中返回类型被描述为“左手操作数的左值”。

同样,我无法确定取消引用是返回指向对象还是对对象的引用。

这些陈述是否等价?如果是这样,那如何?

C++ 赋值运算符 取消引用 左值

评论

1赞 oczkoisse 3/8/2013
请大家注意。感谢您的所有回复,但我并不是要询问操作员过载的问题。我还没到那里,我只是在问用该语言内置的赋值操作。

答:

13赞 Luchian Grigore 3/8/2013 #1

它们都可以是任何东西,但通常通过引用返回当前对象,即operator =

A& A::operator = ( ... )
{
   return *this;
}

是的,“引用左手操作数的类型”和“左值引用左手操作数”的意思是一样的。

取消引用运算符基本上可以具有任何返回类型。这主要取决于程序的逻辑,因为您重载的是应用于对象的运算符,而不是指向该对象的指针。通常,这用于智能指针或迭代器,并返回它们环绕的对象:

struct smart_ptr
{
   T* innerPtr;
   T* smart_ptr::operator* ()
   {
      return innerPtr;
   }
}

smart_ptr p; 
T* x = *p;  

评论

0赞 oczkoisse 3/8/2013
您能否扩展一下“左手操作数的左值”,其含义与“左手操作数的类型”相同。引用是左值吗?
0赞 Luchian Grigore 3/8/2013
@user2148032 是的,引用是左值。左手操作数是对象本身(因为有左手操作数)。x = yx
2赞 bash.d 3/8/2013 #2

我见过类似的问题,但我想最好使用

X& X::operator=(const X&);

使用它,您将能够在链赋值中重用该对象。

45赞 SomeWittyUsername 3/8/2013 #3

该标准正确定义了赋值运算符的返回类型。 实际上,赋值操作本身并不依赖于返回值 - 这就是为什么返回类型不容易理解的原因。

返回类型对于链接操作非常重要。 请考虑以下构造:。这应该等于 ,即 应该分配到 和 into 。将其重写为 .为了使赋值正常工作,返回类型必须引用内部赋值结果(它也可以与复制一起使用,但这只是不必要的开销)。a = b = c;a = (b = c)cbbaa.operator=(b.operator=(c))ab.operator=(c)

取消引用运算符返回类型取决于您的内部逻辑,以适合您需求的方式定义它。

评论

2赞 Luchian Grigore 3/8/2013
“该标准正确定义了赋值运算符的返回类型” - 如何定义?
22赞 SomeWittyUsername 3/8/2013
@LuchianGrigore OP 所引用的:C++11 标准第 5.17 节,其中返回类型被描述为“左手操作数的左值”。
14赞 Javi 3/7/2016
这最后一条评论已经比整个答案更有用了。
0赞 SomeWittyUsername 11/21/2019
我不禁注意到最后一条评论得到的支持程度。我发现有点令人失望的是,许多人发现肤浅的正式注销比解释实际推理的详细答案更有用。是的,从技术上讲,您可以有一个返回值而不是引用的赋值运算符。
1赞 Rob Swarbrick 12/16/2022
@cassepipe 据我了解,赋值运算符返回一个非常量引用,因为在调用代码中,根据定义,您已经分配给此对象,因此必须已经对该对象使用了非常量引用。这避免了将“const”引入这些操作的潜在中断。
0赞 Sami 10/3/2023 #4

链接工作,无需引用:从 operator= 返回副本仍然允许链接赋值(如 a = b = c),因为 operator= 将返回一个临时对象(已分配对象的副本),然后可以在后续赋值中使用该对象。但是,这涉及创建和销毁临时对象,这可能会产生性能开销,尤其是当这些操作对于相关类来说成本很高时。

正如Scott Meyers在他的《有效的C++》一书中提到的:

实现此方式是赋值返回对 它的左边论点,这是你应该遵循的惯例 当您为类实现赋值运算符时。这只是一个惯例;不遵循它的代码将编译。然而 所有内置类型以及 标准库中(或即将加入)中的所有类型 — 参见第 54 项 (例如,字符串、向量、复数、tr1::shared_ptr 等)。除非你有充分的理由以不同的方式做事,否则不要这样做。

下面的代码工作得很好!

#include <iostream>
class Rational {
public:
    // ctor is deliberately not explicit; allows implicit int-to-Rational conversions
    Rational(int numerator = 0, int denominator = 1)
    {

        this->m_numerator = numerator;
        this->m_denominator = denominator;
    }
    int numerator() const  // accessors for numerator 
    {

        return this->m_numerator;
    }
    int denominator() const // accessors for denominator 
    {

        return this->m_denominator;

    }
    Rational operator=(const Rational& rhs)
    {
        std::cout << "operator=(const Rational& rhs)\n";
        this->m_numerator = rhs.m_numerator;
        this->m_denominator = rhs.m_denominator;
        return *this;
    }
    
    private:
    int m_numerator{};
    int m_denominator{};
};

int main()
{
    Rational a(1, 2);
    Rational b(3, 4);
    Rational c(5, 6);
    a = b = c; //  a.operator=(b.operator=(c));
}