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

41
我刚开始学习C++,对于赋值运算符和解引用运算符的返回类型有些困惑。我正在按照《C++ Primer》这本书学习。在不同的场合,作者说赋值运算符的返回类型是左操作数的引用类型,但后来又说返回类型是左操作数的类型。我查阅了C++11标准第5.17节,其中将返回类型描述为“引用左操作数的左值”。
同样地,我无法确定解引用运算符是返回指向的对象还是对象的引用。
这些陈述是否等价?如果是,那么它们之间有什么关系?

1
请大家注意。谢谢大家的回复,但我并不是想问关于运算符重载的问题。我还没有到那一步,我只是在询问语言中内置的赋值操作。 - oczkoisse
4个回答

45
标准正确地定义了赋值运算符的返回类型。实际上,赋值操作本身并不依赖于返回值,这就是为什么返回类型不容易理解的原因。
返回类型对于链接操作很重要。考虑以下构造:a = b = c;。这应该等同于a = (b = c),即将c赋值给b,然后再将b赋值给a。将其重写为a.operator=(b.operator=(c))。为了使对a的赋值正常工作,b.operator=(c)的返回类型必须是对内部赋值结果的引用(使用复制也可以,但那只是一种不必要的开销)。
解引用运算符的返回类型取决于您的内部逻辑,请按照适合您需求的方式定义它。

2
标准正确地定义了赋值运算符的返回类型 - 如何实现? - Luchian Grigore
22
如 OP 引用的 C++11 标准第 5.17 节所述,返回类型被描述为“引用左操作数的 lvalue”。 - SomeWittyUsername
14
这最后一条评论已经比整个答案更有用了。 - Javi
1
如我所理解的,赋值操作符返回一个非const引用,因为在调用代码中,您已经对此对象进行了赋值,因此必须已经使用了非const引用来访问该对象。 这避免了引入“const”到这些操作中可能导致破损的风险。 - Rob Swarbrick
1
@cassepipe 根据我的理解,赋值运算符返回一个非常量引用,因为在调用代码中,你已经在对这个对象进行赋值操作,因此必须已经在使用一个非常量引用来引用这个对象。 这样可以避免在这些操作中引入“const”可能导致的问题。 - undefined
显示剩余3条评论

13
它们都可以是任何东西,但通常 operator = 返回当前对象的引用,即
A& A::operator = ( ... )
{
   return *this;
}

是的,“左操作数类型的引用”和“引用左操作数的lvalue”意思相同。

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

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

smart_ptr p; 
T* x = *p;  

请问您能否详细解释一下“lvalue指左操作数”的含义,它是否与“引用左操作数类型”相同?引用是lvalue吗? - oczkoisse
@user2148032 是的,引用是一个左值。左操作数是对象本身(因为 x = y 的左操作数是 x)。 - Luchian Grigore

2
我看到过类似的问题,但我认为最好使用。
X& X::operator=(const X&);

使用此功能,您将能够在链式赋值中重复使用对象。

0
链式赋值不需要引用: 从operator=返回一个副本仍然允许进行链式赋值(例如 a = b = c),因为operator=将返回一个临时对象(被赋值对象的副本),该对象可以在后续的赋值中使用。然而,这涉及创建和销毁临时对象,这可能会产生性能开销,特别是如果这些操作对于所涉及的类来说是昂贵的。 正如Scott Meyers在他的Effective C++书中提到的: 这个实现的方式是,赋值操作返回其左操作数的引用,这是一个约定,你应该遵循这个约定来实现你的类的赋值操作符。这只是一个约定;不遵循这个约定的代码也可以编译。然而,这个约定被所有内置类型以及标准库中的所有类型(例如string、vector、complex、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));
}

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