为什么链接需要运算符返回引用
Why chaining requires operator to return reference?
应返回引用的最流行的运算符是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++-3.6 new.cxx -o new new.cxx:70:3:错误:没有可行的重载 '=' b=c=a; ~^~~~ new.cxx:34:8:注意:候选函数不可行:第一个参数需要 l 值 Alpha 运算符=(Alpha & other) ^ 生成 1 个错误。
海湾合作委员会这个:
new.cxx:34:8:注意:参数 1 没有从"Alpha"到"Alpha&"的已知转换
但我不明白理论上有什么问题。我认为会发生什么:
- 首先,为
- 对象
c
调用运算符 = 。它通过引用接收对象a
,将其值复制到 c 并返回其自身(对象c
的匿名副本):调用复制 soncstructor。 - 然后为对象
b
调用运算符 = 。它需要右值引用,但我们只写了左值引用,所以会发生错误。
我添加了 rval 运算符= 和复制构造函数,它接收左值引用并且一切正常,现在我不知道为什么(我应该编写接收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;
}
};
值运算符的此签名
Alpha operator=(Alpha& another)
在两个方面是不寻常的。首先是它返回分配给对象的副本。很少这样做。另一个是它接受非常量引用作为参数。
非常量引用使其不接受临时对象作为参数(因为这些对象只会绑定到常量左值引用)。
组合起来,这意味着从第一个operator=
返回的临时不能用作第二个operator=
的参数。
您的选项是返回引用,或使参数Alpha const&
。这两个选项都可以单独工作,也可以组合使用。
如您所发现的,第三个选项是显式添加移动分配运算符,使用专门接受临时运算符的Alpha&&
。
标准方法是声明复制赋值运算符
Alpha& operator=(Alpha const& other);
除非您有非常具体的原因选择其他签名。
此行为是故意的,目的是让我们有机会微调代码:
-
在第一个代码段中,您明确定义了需要左值引用
operator=()
。 就像在你的链中你没有提供这样的引用一样,编译器抱怨。 -
在第二个代码段中,您添加了接受右值引用的重载
operator=()
。 因此,这可以处理按值返回而不会破坏链。
在您的特定情况下,两种替代方案具有完全相同的代码,这就是您不了解问题的原因。 但是对于更复杂的数据结构,具有不同的行为是完全有意义的。 想象一下,在你的类中,你将有一个包含数千个元素的向量成员:
class Alpha {
vector<double> hugevec;
...
};
在引用赋值的情况下,您可以像往常一样执行(因为原始对象必须保持活动状态):
Alpha operator=(Alpha& another)
{
...
hugevec = another.hugevec; // thousands of elements get duplicated
return *this;
}
但是,如果从临时值对象赋值,您可以"窃取"现有向量,因为它无论如何都会被丢弃(您也可以重用其指针成员之一,而不是分配新对象并复制它,并销毁旧对象):
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;
}
因此,编译器所做的这种细微区分可以显著提高性能。
顺便说一下,这不应与通用引用混淆,后者使用相同的&&
语法,但在模板中,并且让编译器选择哪个右值或左值引用最合适。
编辑:您可能会对以下文章感兴趣:
- C++通常签名的运算符重载指南
- 运算符重载:一些 NIC 实现技巧的常见做法。
- 寿命延长从函数返回引用
- 了解C++如何返回引用并绑定到引用
- 返回引用实例和非引用实例(return mystr & vs mystr)之间的区别是什么?
- 从类返回引用向量
- 使用unique_ptr并返回引用,或者我应该使用shared_ptr并在需要时制作副本
- 混淆C++从函数返回引用
- 两个相同的重载运算符[]一个返回引用
- 为什么向量的.at()成员函数返回引用而不是迭代器
- C++使用和返回引用
- 返回 T 引用的 Const 函子禁止赋值
- 返回引用是否也会延长其生存期?
- 如何返回引用,然后递增迭代器
- 如果使用返回引用的函数初始化"auto"var,为什么它不声明引用类型?
- 我是否需要将 ref 与 make_pair 一起使用才能返回引用?
- 在 vector::at 返回引用后进行更改
- 为什么PyImport_ImportModule返回引用计数为 3 而不是 1 的 PyObject*
- 超出返回引用的单一实例生存期
- Boost.Python 返回引用现有 c++ 对象的 python 对象
- C++ 运算符 += 重载返回引用
- 如何声明接受转发引用并返回引用或副本的函数模板