在右值上调用.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上调用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
    }
    

    最佳答案

    直到最近,还不可能通过调用对象的rvalue / lvalue-ness重载.begin()

    从理论上讲,将其添加到标准库中后,如果将其修改,可能会破坏现有代码。

    破坏现有代码是很糟糕的,非常糟糕,以至于遗留了一些古怪的怪癖,以排除相当有力的证据表明此类代码不存在,有明确的诊断信息和/或更改的影响确实有用。

    因此.begin()忽略了其*this的右值性。

    除了可能希望与std::begin兼容之外,对.begin()没有任何限制。

    从理论上讲,标准容器在右值上下文中没有适当的位置来使用std::begin进行调用。与std::move或rvalues交互的“正确”方法是,在调用完成后,您不必考虑移出对象的状态。

    这意味着(从逻辑上来说)您只能获得两个迭代器之一(开始或结束)。

    在这种情况下,正确的语义是一个很大的困惑。我已经编写了适配器,在这种情况下(例如,对rvalue的伪开始/结束调用)生成了移动迭代器(例如),但是这样做通常是非常令人惊讶的,并且我认为这最终是一个不好的举动。

    关于c++ - 成员函数.begin()和std::begin(),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37262329/

    10-14 07:02