我们能否可靠地预递增/递减右值?
Can we reliably pre-increment/decrement rvalues?
例如,std::vector<int>::iterator it = --(myVec.end());
.这在GCC 4.4中有效,但我听到有传言说它不可移植。
仅当std::vector<int>::iterator
是具有operator++
成员函数的对象类型时,它才有效。 如果是标量类型(例如int *
),或者operator++
是非成员函数,它将失败。
5.3.2 递增和递减
1 - 前缀
++
的操作数通过添加1
[...] 进行修改。操作数应为可修改的右值。[...]
2 - [...]前缀--
[...] 的操作数要求与前缀++
的操作数相同。[...]
可以在临时对象上调用非常量非静态成员函数(因为它们具有非const
对象类型,根据 9.3.2p3),但非成员函数中的左值引用参数不能绑定到临时 (13.3.3.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
带有成员operator++
的对象类型实现的,但是您的代码可以很容易地使用相同的编译器和不同的标准库进行编译,其中std::vector<int>::iterator
是int *
,在这种情况下它会失败。
std::vector
,std::array
和std::string
是唯一可以用标量(指针)迭代器明智地实现的容器模板,但这并不意味着在其他容器的迭代器上调用++
是安全的;它们可以具有非成员operator++
,如上所述T
。
要创建对 before-theend 元素的迭代器,请使用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)重载。在前一种情况下,重载--
将编译(成员函数重载的运算符可以应用于右值),而在后一种情况下,它不会。
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 我们可以访问一个不存在的联盟的成员吗
- 如果编译的源代码是特定于它编译的硬件的,我们如何分发它
- 当使用透明的std函数对象时,我们还需要写空的尖括号吗
- 如何在C++中读取空格分隔的输入 当我们不知道输入的数量时
- 我们可以删除链表中静态内存中的节点吗
- 为什么我们要为avl树实现返回一个指向节点的指针,而不是void函数
- 当我们从/tp地址中添加/减去一个整数时会发生什么
- 当我们为(;;) 写作时,它做了什么?for 循环中的双分号有什么作用?
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 当我们进行一些操作时,应该使用什么'std::string'或'std::stringstream'?
- 我们可以将数据永久保存为数据结构吗?
- 为什么我们将单个或多维数组的大小声明为常量值?
- 为什么我们不能重复使用具有不同模板参数的别名模板标识符?
- 为什么当我们有常量引用时创建临时对象?
- 为什么我们再次从结构对象创建结构变量?
- 为什么我们不编写可以处理C++标识符的汇编器和链接器?
- 当返回类型声明为 ListNode 时,我们是否可以返回 false<T>*
- 我们是否需要为 C++ 中的多个函数初始化多个模板?
- 共享记忆:让我们谈谈它的特殊性