成员函数 .begin() 和 std::begin()

Member function .begin() and std::begin()

本文关键字:begin 函数 std 成员      更新时间:2023-10-16

调用 std::vector 的成员函数.begin()和右值上的 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.

  1. 我的理解正确吗?
  2. 为什么std::begin()没有右值重载?

只是一个注释,我用move(a)来演示调用.begin()std::begin()右值。当然,它可以替换为任何定义.begin()std::begin()的 rvalue 对象。

编辑:这是显示我遇到此问题的真实示例。我简化了很多,只是为了传达在右值上调用std::begin()的想法。因此,由于row_matrix是一个代理类,因此在右值上调用beginend应该没有任何问题,因为基础对象是相同的。

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的价值.

std::begin没有这样的限制,除了可能希望与.begin()兼容。

从理论上讲,标准容器对在右值上下文中使用 std::begin 调用没有适当的响应。 与std::move或右值交互的"正确"方式是,在调用完成后,您不应该关心移自对象的状态。

这意味着(逻辑上(您只能取出两个迭代器中的一个(开始或结束(。

在这种情况下,正确的语义是什么是一个很大的困惑。 我已经编写了适配器,在这种情况下(对右值的伪开始/结束调用(会生成移动迭代器(例如(,但总的来说这样做是非常令人惊讶的,我认为这最终是一个糟糕的举动。

23.2.1/12 指出:

除非另有说明(明确或通过定义 函数,就其他函数而言(,调用容器成员 函数或将容器作为参数传递给库函数 不得使迭代器无效或更改对象的值 在该容器内。

DR 2321 还希望明确说明:

没有移动构造函数(或移动赋值运算符 allocator_traits<allocator_type>::propagate_on_container_move_assignment::value 为 true(的容器(array 除外(使任何 引用引用 源容器。[注意:end()迭代器不引用任何 元素,因此它可能无效。— 尾注]

当然,这只有在您移动到不同的容器时才有用,但事实并非如此。