为什么我不能递减 std::array::end()?

Why can't I decrement std::array::end()?

我正在为容器类型创建一个方便的 display() 函数模板。最后一个元素的输出与其余元素不同,因此我检查何时 myIterator != --cont.cend();。这适用于 std::vector,但不适用于 std::array。为什么?

这是一个 MWE(不是我的实际代码):

std::vector<double> vec({1,2});
std::array<double, 2> arr({{1,2}});
auto vecIt = --vec.end(); // OK
auto arrIt = --arr.end(); // error: lvalue required as decrement operand

这是 C++ 标准中的一个事实。

如果 Tstd::vectorstd::liststd::deque,则需要

--T.end() 才能工作。这在 C++11 之前都是正确的;此后有所放松。

这取决于迭代器是如何定义的。

似乎 class 模板 std::array 迭代器被定义为指针。所以功能开始,结束。 cbegin, cend return 只是指针。因此,由于指针是按值 return 编辑的,因此您可能不会减少它,因为需要 lvalue..

对于 class 模板 std::vector 迭代器被定义为 user-defined class,其中定义了运算符 --()。

考虑以下演示程序

#include <iostream>

class Int
{
public:
    Int( int x = 0 ) : x ( x ) 
    {

    }

    Int & operator --()
    {
        --x;
        return *this;
    }

    friend std::ostream & operator <<( std::ostream &os, const Int &i )
    {
        return os << i.x;
    }
private:
    int x;
};

int f( int x )
{
    return x;
}

Int f( Int x )
{
    return x;
}

int main() 
{
    std::cout << --f( Int( 10 ) ) << std::endl;
//  error: lvalue required as decrement operand
//  std::cout << --f( 10 ) << std::endl;

    return 0;
}

考虑到你可以写

auto arrIt = std::prev( arr.end() );

而不是

auto arrIt = --arr.end();

前提是您包括 header <iterator>.

您可以将运算符与 class 模板的反向迭代器一起使用 std::array 因为标准将它们明确定义为

typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

和 user-defined class std::reverse_iterator 定义 operator --() .

这是一个演示程序

#include <iostream>
#include <array>

int main() 
{
    std::array<double, 2> arr = { { 1, 2 } };

    auto it = --arr.rend();

    std::cout << *it << std::endl;

    return 0;
}

永远不要递减右值,即使它恰好可以编译。这对代码的读者来说是不直观的。

改用std::prev

auto it = std::prev(arr.end());

因为这是 , [expr.pre.increment] and [expr.post.increment] 两者都有限制:

The operand shall be a modifiable lvalue.

现在,vec.end()arr.end() 都不是左值,但它们的类型都是实现定义的(对于 array and for vector)。在这两种情况下,一个简单的指针将满足这些容器的所有迭代器要求——这将是一种使用内置前缀和后缀增量的类型。在那种情况下,--c.end() 由于引用的限制将是格式错误的。但是,如果迭代器类型是 class 类型 ,则上述限制不适用 - 因为我们没有使用内置增量运算符 - 并调用 operator--() 在 class 上没有这个限制(尽管它可以,如果成员函数是左值引用限定的)。

所以 --c.end() 不能保证向量或数组的工作,因为如果 end() return 是一个指针,这是错误的,并且 end()允许 return 一个指针。在您的特定实现中,vectoriterator 具有 class 类型,但 arrayiterator 只是指针类型,这就是前者有效的原因,但后者没有。

首选 std::prev(c.end()),它将适用于所有实现的两种容器类型。