为什么运算符[]不为lvalues和rvalues重载?

Kno*_*abe 17 c++ operator-overloading c++11

标准C++容器只提供一个operator[]容器版本,如vector<T>deque<T>.它返回一个T&(除了for vector<bool>,我将忽略),这是一个左值.这意味着在这样的代码中,

vector<BigObject> makeVector();       // factory function

auto copyOfObject = makeVector()[0];  // copy BigObject
Run Code Online (Sandbox Code Playgroud)

copyOfObject将被复制.鉴于makeVector()返回rvalue vector,期望copyOfObject移动构造似乎是合理的.

如果operator[]为rvalue和lvalue对象重载了这样的容器,那么operator[]对于右值容器,可以返回一个右值引用,即一个rvalue:

template<typename T>
container {
public:
    T& operator[](int index) &;       // for lvalue objects
    T&& operator[](int index) &&;     // for rvalue objects
...
};
Run Code Online (Sandbox Code Playgroud)

在这种情况下,copyOfObject将移动构建.

总的来说,这种超载是一个坏主意吗?有没有理由为C++ 14中的标准容器做到这一点?

T.C*_*.C. 4

将评论转换为答案:

这种方法本质上并没有什么问题。类成员访问遵循类似的规则(E1.E2如果E1是右值,则为 xvalue,并且E2命名非静态数据成员并且不是引用,则为 xvalue,请参阅 [expr.ref]/4.2),并且容器内的元素在逻辑上类似于非静态数据成员。

为或其他标准容器执行此操作的一个重要问题std::vector是它可能会破坏一些遗留代码。考虑:

void foo(int &);
std::vector<int> bar();

foo(bar()[0]);
Run Code Online (Sandbox Code Playgroud)

operator[]如果右值向量返回 xvalue,最后一行将停止编译。或者 - 可以说更糟糕 - 如果有foo(const int &)载,它将默默地开始调用该函数。

此外,在容器中返回一堆元素并且只使用一个元素已经相当低效了。可以说,执行此操作的代码可能不太关心速度,因此微小的性能改进不值得引入潜在的破坏性更改。

  • @NirFriedman 我们为此提供了 `std::move_iterator` ,尽管有争议的是,不加区别地呈现右值的迭代器不一定是正确的方法(例如,使用 `copy_if` 和谓词按值获取其参数是正确的如果效率低下,但如果你添加一个移动迭代器,一切都会爆炸)。 (2认同)
  • @NirFriedman `使用 iter_t = std::conditional_t&lt;std::is_rvalue_reference&lt;decltype(vec)&gt;{}, std::move_iterator&lt;decltype(vec.begin()&gt;, decltype(vec.begin())&gt;; std: :copy_if(iter_t{vec.begin()}, iter_t{vec.end()}, out, pred);`?无论如何,从右值中获取迭代器已经足够危险了,我认为这不是一个好主意超出向后兼容性所需的支持。 (2认同)