在右值上调用 std::vector
和 std::begin()
的成员函数 .begin()
会导致不同的输出,如下测试所示:
vector<int> a{ 1, 2, 3 };
vector<int>::iterator it1 = move(a).begin(); // OK
vector<int>::const_iterator it2 = move(a).begin(); // OK
vector<int>::iterator it3 = begin(move(a)); // Error!
vector<int>::const_iterator it4 = begin(move(a)); // OK
这是我的理解:std::begin()
调用 const&
重载(因为它缺少 &&
重载),因此,它返回一个 const_iterator 对象。因此,返回值可以分配给 const_iterator
,但不能分配给 iterator。
- 我的理解正确吗?
- 为什么
std::begin()
没有右值重载?
请注意,我使用 move(a)
来演示对右值调用 .begin()
和 std::begin()
。当然,它可以被任何定义良好的 .begin()
和 std::begin()
的右值对象替换。
编辑:这是真实的示例,显示了我在哪里遇到此问题。我已经简化了很多,只是为了传达在右值上调用 std::begin() 的想法。因此,由于 row_matrix 是一个代理类,因此在右值上调用 begin 和 end 应该不会有任何问题,因为底层对象是相同的。
class matrix_row;
class row_iterator;
class matrix {
public:
matrix_row row(int i);
// other members
};
class matrix_row { // <- proxy class representing a row of matrix
public:
row_iterator begin();
row_iterator end();
// other members
private:
int row_;
matrix& matrix_;
};
class row_iterator {
// defined everything needed to qualify as a valid iterator
};
matrix m(3,4);
for(auto x = m.row(1).begin(); x != m.row(1).end(); ++x) {
*x /=2; // OK
}
for(auto x = begin(m.row(1)); x != end(m.row(1)); ++x) {
*x /= 2; // Error
}
最佳答案
直到最近,通过调用对象的右值/左值重载 .begin()
仍然是不可能的。
添加后,理论上,将此类更改添加到标准库中可能会破坏现有代码。
破坏现有代码是很糟糕的,糟糕到足以留下遗留的怪癖,除非有相当有力的证据表明此类代码不存在,会有明确的诊断,和/或更改的效果确实有用。
因此 .begin()
忽略其 *this
的右值。
除了可能希望与 .begin()
兼容之外,对 std::begin
没有这样的限制。
理论上,标准容器对于在右值上下文中使用 std::begin
调用没有适当的响应。与 std::move 或右值交互的“正确”方式是,您不应该关心调用完成后移出对象的状态。
这意味着(逻辑上)您只能获取两个迭代器之一(开始或结束)。
在这种情况下,正确的语义是什么是一个很大的困惑。我编写了适配器,在这种情况下(例如,对右值的伪开始/结束调用)生成 move 迭代器,但这样做通常非常令人惊讶,我认为这最终是一个糟糕的举动。
关于c++ - 成员函数 .begin() 和 std::begin(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37262329/