为什么我不能递减 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++ 标准中的一个事实。
如果 T
是 std::vector
、std::list
或 std::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());
因为这是 language-lawyer, [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 一个指针。在您的特定实现中,vector
的 iterator
具有 class 类型,但 array
的 iterator
只是指针类型,这就是前者有效的原因,但后者没有。
首选 std::prev(c.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++ 标准中的一个事实。
如果T
是 std::vector
、std::list
或 std::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());
因为这是 language-lawyer, [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 一个指针。在您的特定实现中,vector
的 iterator
具有 class 类型,但 array
的 iterator
只是指针类型,这就是前者有效的原因,但后者没有。
首选 std::prev(c.end())
,它将适用于所有实现的两种容器类型。