应该返回引用的最受欢迎的运算符是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;'没有已知的转换
但我无法理解理论上的问题是什么。我认为发生了什么:
c
调用operator =。它通过引用接收对象a
,将其值复制到c并返回它的自身(对象c
)的匿名副本:调用复制soncstructor。b
调用operator =。它需要rvalue ref,但我们只写了左值引用,因此发生了错误。我添加了rval operator =和copy构造函数,它接收左值引用并且一切正常,现在我不知道为什么(我应该编写接收const Alpha& s
或Alpha&& 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;
}
};
答案 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或左值引用最合适。
编辑: 您可能会对以下文章感兴趣: