为什么 operator[] 不为左值和右值重载?
Why isn't operator[] overloaded for lvalues and rvalues?
标准 C++ 容器只为 vector<T>
和 deque<T>
等容器提供一个版本的 operator[]
。它 return 是一个 T&
(除了 vector<bool>
,我将忽略它),这是一个左值。这意味着在这样的代码中,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject
将被复制构造。鉴于 makeVector()
return 是一个右值 vector
,期望 copyOfObject
被移动构造似乎是合理的。
如果此类容器的 operator[]
为右值和左值对象重载,则右值容器的 operator[]
可以 return 右值引用,即右值:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
在这种情况下,copyOfObject
将被移动构造。
有没有理由认为这种重载通常不是一个好主意?为什么 C++14 中的标准容器没有这样做?
我认为如果您移出其中一个元素,您将使容器处于无效状态,我认为根本不需要允许该状态。其次,如果你需要它,你不能像这样调用新对象的移动构造函数:
T copyObj = std::move(makeVector()[0]);
更新:
最重要的一点是,在我看来,容器本质上就是容器,所以它们不应该以任何方式修改其中的元素。它们只是提供存储、迭代机制等
正在将评论转化为答案:
这种方法本身并没有错; class 成员访问遵循类似的规则(如果 E1
是右值并且 E2
命名非静态数据成员并且不是引用,则 E1.E2
是一个 xvalue,请参阅 [ expr.ref]/4.2),容器内的元素在逻辑上类似于非静态数据成员。
为 std::vector
或其他标准容器执行此操作的一个重要问题是它可能会破坏一些遗留代码。考虑:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
如果 operator[]
在右值向量上返回一个 xvalue,最后一行将停止编译。或者 - 可以说更糟 - 如果存在 foo(const int &)
重载,它将开始默默地调用该函数。
另外,在一个容器中返回一堆元素,只使用一个元素已经很低效了。有争议的是,执行此操作的代码可能不太关心速度,因此小的性能改进不值得引入潜在的破坏性更改。
标准 C++ 容器只为 vector<T>
和 deque<T>
等容器提供一个版本的 operator[]
。它 return 是一个 T&
(除了 vector<bool>
,我将忽略它),这是一个左值。这意味着在这样的代码中,
vector<BigObject> makeVector(); // factory function
auto copyOfObject = makeVector()[0]; // copy BigObject
copyOfObject
将被复制构造。鉴于 makeVector()
return 是一个右值 vector
,期望 copyOfObject
被移动构造似乎是合理的。
如果此类容器的 operator[]
为右值和左值对象重载,则右值容器的 operator[]
可以 return 右值引用,即右值:
template<typename T>
container {
public:
T& operator[](int index) &; // for lvalue objects
T&& operator[](int index) &&; // for rvalue objects
...
};
在这种情况下,copyOfObject
将被移动构造。
有没有理由认为这种重载通常不是一个好主意?为什么 C++14 中的标准容器没有这样做?
我认为如果您移出其中一个元素,您将使容器处于无效状态,我认为根本不需要允许该状态。其次,如果你需要它,你不能像这样调用新对象的移动构造函数:
T copyObj = std::move(makeVector()[0]);
更新:
最重要的一点是,在我看来,容器本质上就是容器,所以它们不应该以任何方式修改其中的元素。它们只是提供存储、迭代机制等
正在将评论转化为答案:
这种方法本身并没有错; class 成员访问遵循类似的规则(如果 E1
是右值并且 E2
命名非静态数据成员并且不是引用,则 E1.E2
是一个 xvalue,请参阅 [ expr.ref]/4.2),容器内的元素在逻辑上类似于非静态数据成员。
为 std::vector
或其他标准容器执行此操作的一个重要问题是它可能会破坏一些遗留代码。考虑:
void foo(int &);
std::vector<int> bar();
foo(bar()[0]);
如果 operator[]
在右值向量上返回一个 xvalue,最后一行将停止编译。或者 - 可以说更糟 - 如果存在 foo(const int &)
重载,它将开始默默地调用该函数。
另外,在一个容器中返回一堆元素,只使用一个元素已经很低效了。有争议的是,执行此操作的代码可能不太关心速度,因此小的性能改进不值得引入潜在的破坏性更改。