在rvalues上调用.begin()
和std::vector
的成员函数std::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)
来演示对rvalues调用.begin()
和std::begin()
。当然,它可以替换为.begin()
和std::begin()
定义良好的任何右值对象。
编辑:以下是显示我遇到此问题的真实示例。我已经简化了很多,只是为了传达一个在左值上调用std::begin()
的想法。因此,由于row_matrix
是一个代理类,因为底层对象是相同的,所以在rvalues上调用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
}
答案 0 :(得分:3)
直到最近,无法通过调用对象的rvalue / lvalue-ness重载OData
。
添加时,将这些更改改装到标准库中,理论上可能会破坏现有代码。
破坏现有代码是坏的,足够糟糕,遗留的怪癖留下了相当强有力的证据,证明这些代码不存在,有明确的诊断,和/或改变的效果真的很有用。
因此myOdata.xsodata
会忽略其.begin()
的右值。
.begin()
没有此类限制,除了可能希望与*this
兼容。
理论上,标准容器没有在右值上下文中使用std::begin
调用的适当权限。与.begin()
或rvalues交互的“正确”方式是,在调用完成后,您不应该关心移动对象的状态。
这意味着(逻辑上)你只能得到两个迭代器中的一个(开始或结束)。
在这种情况下,正确的语义是一个很大的困惑。我已经编写了适配器,在这种情况下(rvalue上的伪开始/结束调用)生成移动迭代器(例如),但这样做通常是非常令人惊讶的,我认为这最终是一个糟糕的举动。
答案 1 :(得分:-1)
23.2.1 / 12州:
除非另有说明(明确或通过定义 函数就其他函数而言),调用一个容器成员 函数或将容器作为参数传递给库函数 不得使迭代器无效或更改对象的值 在那个容器内。
DR 2321也希望明确指出:
没有移动构造函数(或移动赋值运算符时
allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
如果容器(array
除外)使任何容器无效 引用,指针或迭代器引用的元素 源容器。 [注意:end()
迭代器不引用任何 元素,因此可能无效。 - 结束说明]
当然,只有将移动到不同的容器中时,这才有用,但事实并非如此。