为什么我可以在begin()上使用赋值运算符,即使它是一个右值?
一段时间以来,我似乎无法解决这个特定问题。例如,如果我有以下代码:
void foo(std::vector<int>::iterator &it) {
// ...
}
int main(){
std::vector<int> v{1,2,3};
foo(v.begin());
}
我会得到编译错误:
initial value of reference to non-const must be an lvalue.
我的猜测是我收到错误,因为a.begin()返回了一个右值。
如果是这样,以下表达式怎么可能起作用:
v.begin()=v.begin()++;
如果v.begin()是右值?
回答
原因是历史的。在该语言的最初几天,用户代码根本无法表达类型的复制赋值运算符应该只对左值起作用。当然,这仅适用于用户定义的类型;对于内置类型分配给 r 值一直是被禁止的。
int{} = 42; // error
因此,对于标准库中的所有类型,复制赋值仅适用于 r 值。我不相信这会做任何有用的事情,所以如果你写这个几乎肯定是一个错误,但它确实可以编译。
std::string{} = "hello"s; // ok, oops
从 返回的迭代器类型也是如此v.begin()。
从 C++11 开始,语言中添加了表达这一点的能力。所以现在我们可以写一种更合理的类型,如下所示:
struct S
{
S& operator=(S const &) && = delete;
// ... etc
};
现在禁止分配给 r 值。
S{} = S{}; // error, as it should be
有人可能会争辩说,应该更新所有标准库类型以做明智的事情。这可能需要大量改写,并破坏现有代码,因此可能不会更改。