JeJ*_*eJo 4 c++ iterator operator-overloading c++-standard-library language-lawyer
在双向迭代器有没有像奢侈品随机访问迭代器,因此需要依赖于std::next和std::prev,当有人需要做的操作,如
std::set<int> s{ 1, 2, 3, 4, 5 };
//std::set<int> s2(s.cbegin(), s.cbegin() + 2); // won't work as there is no operator+ for std::set::const_iterator
std::set<int> s2(s.cbegin(), std::next(s.cbegin(), 2));
//std::set<int> s3(s.cbegin(), s.cend() - 2);  // won't work as there is no operator- for std::set::const_iterator
std::set<int> s3(s.cbegin(), std::prev(s.cend(), 2));
但是,我们可以实现这些 operator+并operator-使用上面的std::next和std::prev.
#include <set>
#include <iterator>  // std::iterator_traits, std::next, std::prev
template<typename InputIt>
constexpr InputIt operator+(InputIt it,
    typename std::iterator_traits<InputIt>::difference_type n)
{
    return std::next(it, n);
}
template<typename InputIt>
constexpr InputIt operator-(InputIt it,
    typename std::iterator_traits<InputIt>::difference_type n)
{
    return std::prev(it, n);
}
int main()
{
    std::set<int> s{ 1, 2, 3, 4, 5 };
    std::set<int> s2(s.cbegin(), s.cbegin() + 2); // works now
    std::set<int> s3(s.cbegin(), s.cend() - 2);   // works now
}
更新:
InputIt正如@Nicol Bolas在他的回答中提到的那样,使用将是一个错误。我想证明这个想法。无论如何,让我们考虑一个  SFINE ed 解决方案,它只接受双向迭代器。除了尼科尔提到的,还有其他问题吗?
#include <type_traits>
#include <iterator>  // std::iterator_traits, std::bidirectional_iterator_tag, std::next, std::prev
template<typename BidirIterators>
constexpr auto operator+(BidirIterators it,
   typename std::iterator_traits<BidirIterators>::difference_type n)
   -> std::enable_if_t<
         std::is_same_v<
            std::bidirectional_iterator_tag,
            typename std::iterator_traits<BidirIterators>::iterator_category
         >, BidirIterators
   >
{
   return std::next(it, n);
}
template<typename BidirIterators>
constexpr auto operator-(BidirIterators it,
   typename std::iterator_traits<BidirIterators>::difference_type n)
   -> std::enable_if_t<
         std::is_same_v<
            std::bidirectional_iterator_tag,
            typename std::iterator_traits<BidirIterators>::iterator_category
          >, BidirIterators
   >
{
   return std::prev(it, n);
}
如果没有,在 C++ 标准库中不是很好吗?
不。
如果迭代器提供编号的递增和递减操作,那么它就是在宣传这些操作是快速的,最好是分摊 O(1) 操作。这就是为什么您希望人们清楚说明std::next/prev:这样对于编写/阅读代码的人来说,所讨论的操作可能很慢是完全显而易见的。也就是说,即使用户传递了一个可以快速完成的迭代器,您的算法也会识别出用户可能传递的迭代器在这方面可能很慢。
在双向或较小的迭代器上提供这样的运算符是在撒谎。这也是为什么你不应该将这样的运算符强加给任意迭代器的原因。
但是还有其他原因导致您的代码错误。
纯输入迭代器(这可能是您所期望的,因为您调用了模板参数InputIt)不需要递减,因此std::prev无效。它必须是一个双向迭代器。
但还有更多。前向迭代器可以被复制,各种副本被认为是一个范围内的独立位置。纯输入迭代器不是这种情况;如果您复制它们,则假定增加一个副本会使该迭代器的所有其他副本无效。所以用户在尝试使用它时必须知道这一点,并注意不要假设单独的迭代器指向不同的位置。
但这通常不是指针的工作方式,是吗?所以再一次,您的迭代器似乎对用户撒谎:它声称提供了它没有提供的功能。