【问题标题】:Why can I use assignment operator on begin() even if it is an rvalue?为什么即使它是右值,我也可以在 begin() 上使用赋值运算符?
【发布时间】:2020-12-27 00:10:44
【问题描述】:

我似乎暂时无法解决这个特定问题。 例如,如果我有以下代码:

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() 是右值?

【问题讨论】:

  • 在类类型的临时对象上调用非常量成员函数是合法的。如v.begin().operator=(v.begin().operator++())。它有时很有用,但当这些成员函数是重载的运算符时,它确实会导致奇怪的代码编译。
  • 如果他们没有指定迭代器应该禁止在标准中分配右值,这对我来说似乎是一个缺陷
  • foo 应该按值获取迭代器。这就是迭代器的用途。传递对迭代器的引用是非常不寻常的。
  • @alterigel OP 关心为什么分配给右值会编译。它至少在 C++14 模式下在 gcc 上编译,所以它与讨厌的 MS 扩展无关

标签: c++


【解决方案1】:

原因是历史性的。在语言的最初阶段,用户代码根本无法表达类型的复制赋值运算符应该只适用于左值。当然,这仅适用于用户定义的类型;始终禁止将内置类型分配给 r 值。

int{} = 42; // error

因此,对于标准库中的所有类型,复制赋值只对 r 值“起作用”。我不相信这有什么用,所以如果你写它几乎肯定是一个错误,但它确实可以编译。

std::string{} = "hello"s; // ok, oops

v.begin() 返回的迭代器类型也是如此。

从 C++11 开始,语言中添加了表达这一点的能力。所以现在可以像这样写一个更明智的类型:

struct S
{
  S& operator=(S const &) && = delete;
  // ... etc
};

现在禁止赋值给右值。

S{} = S{}; // error, as it should be

有人可能会争辩说,所有标准库类型都应该更新以做明智的事情。这可能需要大量的改写,以及破坏现有代码,因此可能不会更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-15
    • 2013-09-22
    • 1970-01-01
    • 2017-12-11
    • 2019-10-08
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多