例如,std::vector<int>::iterator it = --(myVec.end());
。这在 GCC 4.4 中有效,但我听说它不是可移植的。
3 回答
仅当它是具有成员函数std::vector<int>::iterator
的对象类型时才有效。operator++
如果它是标量类型(例如int *
),或者operator++
是非成员函数,它将失败。
5.3.2 递增和递减[expr.pre.incr]
++
1 -通过添加1
[...]来修改前缀的操作数。操作数应为可修改的左值。[...]
2 - [...] prefix--
[...] 对操作数的要求与 prefix 相同++
。[...]
可以在临时对象上调用非常量非静态成员函数(因为它们具有非const
对象类型,根据 9.3.2p3),但非成员函数中的左值引用参数不能绑定到临时对象(13.3.3.1.1)。 4p3)。
struct S { S &operator++(); };
struct T { }; T &operator++(T &);
typedef int U;
++S(); // OK
++T(); // fails
++U(); // fails
这意味着它与编译器无关,而是与标准库有关;正如您所观察到的,libstdc++ 是使用std::vector<int>::iterator
带有 member 的对象类型实现的operator++
,但是您的代码可以很容易地使用相同的编译器和不同的标准库 where std::vector<int>::iterator
is进行编译int *
,在这种情况下它会失败。
std::vector
,std::array
并且std::string
是唯一可以用标量(指针)迭代器实现的容器模板,但这并不意味着调用++
其他容器的迭代器是安全的;operator++
他们可以有上述的非成员T
。
要对 before-the-end 元素创建迭代器,请使用std::prev
:
std::vector<int>::iterator it = std::prev(myVec.end());
std::prev
并且std::next
在 C++11 中是新的,但在 C++03 中很容易实现。
不,这通常不起作用。
在 C++11 中,我们有:auto it = std::prev(myVec.end());
,它工作可靠。
如果你在 C++03 中,Boost 也有类似的功能,尽管写起来很简单:
template <typename BidirectionalIterator>
BidirectionalIterator
prev(BidirectionalIterator x,
typename std::iterator_traits<BidirectionalIterator>::difference_type n = 1)
{
std::advance(x, -n);
return x;
}
请记住,您至少需要该范围内的一个元素才能使其有意义。
这是您的方法通常无法正常工作的示例,请考虑以下精简std::vector<>
:
#include <iterator>
namespace std_exposition
{
template <typename T>
struct vector
{
// this is compliant:
typedef T* iterator;
iterator end()
{
return std::end(data);
}
T data[4];
};
// manually implemented std::prev:
template <typename BidirectionalIterator>
BidirectionalIterator
prev(BidirectionalIterator x,
typename std::iterator_traits<BidirectionalIterator>::difference_type n = 1)
{
std::advance(x, -n);
return x;
}
}
测试程序:
int main()
{
std_exposition::vector<int> myVec;
// Won't compile (method in question):
auto it0 = --(myVec.end());
// Compiles
auto it1 = std::prev(myVec.end());
auto it2 = std_exposition::prev(myVec.end());
}
也有相应std::next
的,在这里实现:
template <typename BidirectionalIterator>
BidirectionalIterator
next(BidirectionalIterator x,
typename std::iterator_traits<BidirectionalIterator>::difference_type n = 1)
{
std::advance(x, n);
return x;
}
这确实是不可移植的,因为没有办法知道是否myVec.end()
返回一个类类型的对象,其运算符--
由成员函数或其他东西重载(甚至可能是一个常规的原始 ponter)。在前一种情况下,重载的--
将编译(成员函数重载的运算符可以应用于右值),而在后一种情况下则不会。