为什么链接要求操作员返回参考?

时间:2016-02-21 10:09:18

标签: c++ operator-overloading

应该返回引用的最受欢迎的运算符是operator=

class Alpha
{
    int x;
    int y;
    std::string z;
  public:
    void print()
        { cout << "x " << x << " y " << y << "  " << z << '\n'; }
    Alpha(int xx=0, int yy=0, std::string zz=""): x(xx), y(yy), z(zz) {}
    Alpha operator=(Alpha& another)
    {
        x = another.x;
        y = another.y;
        z = another.z;
        return *this;
    }


};

int main()
{
    Alpha a(3,4,"abc"),b,c;
    b=c=a;
    return 0;
}

Clang说:

  

clang ++ - 3.6 new.cxx -o new new.cxx:70:3:错误:没有可行的重载   '='           B = C = A;           〜^ ~~~ new.cxx:34:8:注意:候选函数不可行:期望第一个参数的l值           Alpha运算符=(Alpha&amp; another)                 生成^ 1错误。

gcc:

  

new.cxx:34:8:注意:参数1从'Alpha'到'Alpha&amp;'没有已知的转换

但我无法理解理论上的问题是什么。我认为发生了什么:

  1. 首先,对象c调用operator =。它通过引用接收对象a,将其值复制到c并返回它的自身(对象c)的匿名副本:调用复制soncstructor。
  2. 然后为对象b调用operator =。它需要rvalue ref,但我们只写了左值引用,因此发生了错误。
  3. 我添加了rval operator =和copy构造函数,它接收左值引用并且一切正常,现在我不知道为什么(我应该编写接收const Alpha& sAlpha&& s的rvalue复制构造函数):

    class Alpha
    {
        int x;
        int y;
        std::string z;
      public:
        void print()
        { cout << "x " << x << " y " << y << "  " << z << '\n'; }
        Alpha(int xx=0, int yy=0, std::string zz=""): x(xx), y(yy), z(zz) {}
        //Alpha(Alpha&& s): x(s.x), y(s.y), z(s.z) {}
        Alpha(Alpha&& ) = delete;
        Alpha(Alpha& s): x(s.x), y(s.y), z(s.z) {}
        Alpha operator=(Alpha& another)
        {
            x = another.x;
            y = another.y;
            z = another.z;
            return *this;
        }
        Alpha operator=(Alpha&& another)
        {
            x = another.x;
            y = another.y;
            z = another.z;
            return *this;
        }
    
    };
    

2 个答案:

答案 0 :(得分:7)

赋值运算符的此签名

Alpha operator=(Alpha& another)

两种方式不常见。第一个是它返回指定对象的副本。很少这样做。另一个是它接受非const引用作为参数。

非const引用使得它不接受临时对象作为参数(因为它们只会绑定到 const 左值引用)。

在组合中,这意味着从第一个operator=返回的临时值不能用作第二个operator=的参数。

您的选择是返回引用,还是创建参数Alpha const&。这两个选项都可以单独使用,也可以组合使用。

正如您所发现的,第三个选项是使用专门接受临时值的Alpha&&显式添加移动赋值运算符。

标准方法是声明复制赋值运算符

Alpha& operator=(Alpha const& other);

除非您有非常选择其他签名的具体原因。

答案 1 :(得分:1)

此行为是故意制作的,为我们提供了微调代码的机会:

  • 在第一个代码段中,您明确定义了operator=()以要求左值引用。在你的链中,你没有提供这样的引用,编译器抱怨。

  • 在第二个代码段中,您添加了operator=()的重载,该重载接受右值引用。因此,这可以在不破坏链的情况下处理按值返回。

在您的具体情况下,两种选择都具有完全相同的代码,这就是您不理解问题的原因。但是对于更复杂的数据结构,拥有不同的行为是完全有意义的。想象一下,你将在你的班级中拥有数千个元素的向量成员:

class Alpha {
    vector<double> hugevec; 
    ...
};

如果是参考作业,你会照常进行(因为原始对象必须保持活着):

Alpha operator=(Alpha& another)
{
    ...
    hugevec = another.hugevec;  // thousands of elements get duplicated
    return *this;
}

但是如果从临时值对象进行赋值,你可以“窃取”exising向量,因为它无论如何都会被丢弃(你也可以重用它的一个指针成员,而不是分配一个新对象来复制它,并摧毁旧的):

Alpha operator=(Alpha&& another)
{
    ...
    swap (another.hugevec);  // no elements get duplicated !
                             // the old elements are now in the temporary 
                             // which will take care of their destruction
    return *this;
}

因此,编译器所做的这种微妙区别可以显着提高性能。

顺便说一句,这不应该与使用相同&&语法但在模板中的通用引用混淆,并且让编译器选择哪个rvalue或左值引用最合适。

编辑: 您可能会对以下文章感兴趣: