(注意原始问题标题有“而不是rvalue”而不是“而不是const引用”。下面的答案之一是对旧标题的回应。为清楚起见,这是固定的)
C和C ++中的一个常见结构是用于链式分配,例如,
int j, k;
j = k = 1;
首先执行第二个=
,表达式k=1
具有k
设置为1的副作用,而表达式本身的值为1。
但是,一个在C ++中合法(但不在C中)的构造如下,它对所有基类型都有效:
int j, k=2;
(j=k) = 1;
这里,表达式j=k
具有将j
设置为2的副作用,并且表达式本身成为j
的引用,然后j
将j=k
设置为1.据我所知,这是因为表达式const
返回非int&
Meyers Effective C++,例如const
。一般来说是一个左值。
对于用户定义的类型,通常也建议使用此约定,如“第10项:赋值运算符在const
(括号加法矿)中返回* this的引用(非常量)”中所述。本书的这一部分并不试图解释为什么参考文献是非(j=k) = 1;
,或者甚至注意到非#include <iostream>
using std::cout;
struct X{
int k;
X(int k): k(k){}
const X& operator=(const X& x){
// the first const goes against convention
k = x.k;
return *this;
}
};
int main(){
X x(1), y(2), z(3);
x = y = z;
cout << x.k << '\n'; // prints 3
}
ness传递。
当然,这肯定会增加功能,但(j=k) = 1
声明似乎至少可以说是尴尬。
如果约定具有内置赋值返回const引用,那么自定义类也将使用此约定,并且C中允许的原始链式构造仍然可以工作,而没有任何无关的副本或移动。例如,以下运行正确:
(a = b) = c
优点是所有3(C内置,C ++内置和C ++自定义类型)都是一致的,不允许像int a, b = 42;
int *p = &(a = b);
这样的习语。
故意在C和C ++之间添加这个成语吗?如果是这样,哪种情况可以证明其使用的合理性?换句话说,这种功能扩展能带来什么非虚假的好处呢?
根据设计,C和C ++之间的一个基本区别是C是一种丢失左值的语言,而C ++是一种保留左值的语言。
在C ++ 98之前,Bjarne添加了对该语言的引用,以便使运算符重载成为可能。为了有用,引用要求保留表达式的左值而不是丢弃。
直到C ++ 98,这种保留左值的想法才真正形式化。在C ++ 98标准之前的讨论中,引用要求保留表达式的左值的事实被注意到并形式化,并且当C ++从C进行一次主要且有目的的中断并成为左值保留语言时。
只要有可能,C ++就会努力保留任何表达式结果的“左值”。它适用于所有内置运算符,也适用于内置赋值运算符。当然,没有办法像const
那样编写表达式,因为它们的行为是未定义的(至少在原始的C ++标准下)。但是由于C ++的这个属性,你可以编写类似的代码
class vector {
vector& operator=(const vector& other) {
// Do some heavy internal copying here.
// No copy here: I just effectively return this.
return *this;
}
};
这是一个多么有用的问题,但同样,这只是保留左值的C ++表达式设计的一个结果。
至于为什么它不是一个class vector {
vector operator=(const vector& other) {
// Do some heavy stuff here to update this.
// A copy must happen here again.
return *this;
}
};
左值...坦率地说,我不明白为什么它应该是。与C ++中任何其他保留左值的内置运算符一样,它只保留给它的任何类型。
我会在标题中回答这个问题。
我们假设它返回了一个右值引用。以这种方式返回对新分配的对象的引用是不可能的(因为它是一个左值)。如果无法返回对新分配的对象的引用,则需要创建副本。对于重物,例如容器,这将是非常低效的。
考虑一个类似于std :: vector的类的示例。
使用当前返回类型,赋值以这种方式工作(我不是故意使用模板和复制交换习惯用法来保持代码尽可能简单):
*this
我们假设它返回了一个右值:
a = b = c
您可能会考虑返回一个右值引用,但这也不会起作用:您不能只移动b
(否则,const vector&
将运行k + 1
分配链),因此还需要第二个副本才能返回它。
你的帖子正文中的问题是不同的:返回一个int
确实是可能的,没有上面显示的任何复杂性,所以它看起来更像是一个惯例。
注意:问题的标题是指内置插件,而我的答案涵盖自定义类。我相信这是关于一致性的。如果内置和自定义类型的行为不同,那将是非常令人惊讶的。
内置运算符不会“返回”任何内容,更不用说“返回引用”了。
表达式主要由两件事组成:
例如,k = 1
具有类型int
和值类别“prvalue”,但k = 1
具有类型int k;
和值类别“lvalue”。左值是表示存储位置的表达式,k = 1
指定的位置与声明int
分配的位置相同。
C标准只有值“lvalue”和“not lvalue”。在C k = 1
有类型const int
和类别“不左值”。
你似乎在暗示lvalue
应该有类型Should implicitly generated assignment operators be & ref-qualified?和值类别void foo(int& x);
int y;
foo(y = 3);
。也许它可以,语言会略有不同。它会禁止令人困惑的代码,但也可能禁止使用有用的代码。这是一个语言设计师或设计委员会难以评估的决定,因为他们无法想到语言可以使用的所有可能方式。
他们犯了错误,因为他们没有引入可能导致问题的限制。一个相关的例子是y
。
想到的一种可能情况是:
3
将foo
设置为y = 3; foo(y);
,然后调用qazxswpoi。根据你的建议,这是不可能的。当然你可以争辩说qazxswpoi无论如何更清楚,但这是一个滑坡:也许不应该允许增量运算符在更大的表达式等内等。