我们能否可靠地预先递增/递减rvalues?

时间:2012-11-30 18:53:37

标签: c++ rvalue pre-increment

例如,std::vector<int>::iterator it = --(myVec.end());。这适用于GCC 4.4,但我听说有传言称它不便携。

3 个答案:

答案 0 :(得分:8)

仅当std::vector<int>::iterator是具有operator++成员函数的对象类型时,它才有效。如果它是标量类型(例如int *),或者operator++是非成员函数,则它将失败。

  

5.3.2递增和递减[expr.pre.incr]

     

1 - 通过添加++ [...]修改前缀1的操作数。 操作数应为可修改的左值。 [...]
  2 - [...]对前缀-- [...]的操作数的要求与前缀++的要求相同。 [...]

非const非静态成员函数可以在临时对象上调用(因为它们具有非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 *的不同标准库轻松编译1}},在这种情况下会失败。

std::vectorstd::arraystd::string是唯一可以通过标量(指针)迭代器明智地实现的容器模板,但这并不意味着调用++在其他容器上的迭代器是安全的;他们可以将非成员operator++作为T以上。

要创建前端元素的迭代器,请使用std::prev

std::vector<int>::iterator it = std::prev(myVec.end());

std::prevstd::next是C ++ 11中的新功能,但很容易在C ++ 03中实现。

答案 1 :(得分:6)

不,这不会起作用。

在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;
}

答案 2 :(得分:2)

这确实不可移植,因为没有办法知道myVec.end()是否返回类类型的对象,其中运算符--被成员函数或其他东西重载(甚至可能是常规的原始ponter) 。在前一种情况下,重载的--将被编译(由成员函数重载的运算符可以应用于rvalues),而在后一种情况下则不会。(/ p>