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

时间:2015-03-27 22:26:49

标签: c++ c++11 operator-overloading

标准C ++容器仅为operator[]vector<T>等容器提供deque<T>的一个版本。它会返回T&vector<bool>除了我将要忽略的),这是一个左值。这意味着在像这样的代码中,

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

auto copyOfObject = makeVector()[0];  // copy BigObject

copyOfObject将被复制构建。鉴于makeVector()返回右值vector,期望copyOfObject移动构建似乎是合理的。

如果对于rvalue和lvalue对象重载此类容器的operator[],则对于右值容器,operator[]可以返回一个右值引用,即一个右值:

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

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

这种超载是否总体上是一个坏主意?有没有理由为C ++ 14中的标准容器做不到?

2 个答案:

答案 0 :(得分:3)

将评论转换为答案:

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

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

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

foo(bar()[0]);

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

此外,在容器中返回一堆元素并且仅使用一个元素已经相当低效。可以说,执行此操作的代码可能并不关心速度,因此小的性能提升不值得引入潜在的重大变化。

答案 1 :(得分:0)

我认为如果移出其中一个元素,你会将容器置于无效状态,我认为需要允许该状态。其次,如果你需要它,你不能像这样调用新对象的移动构造函数:

T copyObj = std::move(makeVector()[0]);

更新

最重要的一点是,在我看来,容器本质上是容器,所以它们不应该修改它们内部的元素。它们只是提供存储,迭代机制等。