向双向迭代器提供 operator+ 或 operator- 有什么缺点吗?

Is there any drawbacks in providing operator+ or operator- to bidirectional iterators?

bidirectional iterators have no luxuries like random access iterators, and hence need to depend upon the std::next and 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));

但是我们can implement thoseoperator+operator-使用上面的std::nextstd::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 是一个错误,因为 mentioned in his answer. I thought to demonstrate the idea though. Anyways, let us consider a SFINE ed solution instead 只接受双向迭代器。除了 Nicol 提到的问题,还会有其他问题吗?

#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);
}

If not, isn't nice to have in the C++ standard library?

没有

如果迭代器提供编号的递增和递减操作,则表明此类操作是 快速,最好是分期 O(1) 操作。这就是为什么您希望人们拼出 std::next/prev:这样对于某些人 writing/reading 代码来说,有问题的操作可能很慢是完全显而易见的。也就是说,即使用户传递了一个可以快速执行的迭代器,您的算法也会识别出用户可能传递了一个可能会很慢的迭代器。

在双向或更小的迭代器上提供此类运算符是在撒谎。这也是为什么您不应将此类运算符强加于任意迭代器的原因。

但是还有其他原因导致您的代码错误。

纯输入迭代器(这可能是您所期望的,因为您调用了模板参数 InputIt)不需要递减,因此 std::prev 无效。它必须是双向迭代器。

但还有更多。前向迭代器可以被复制,不同的副本被认为是一个范围内的独立位置。纯输入迭代器不是这种情况;如果您复制它们,则假设递增一个副本会使该迭代器的所有其他副本无效。因此,用户在尝试使用它时必须知道这一点,并注意不要假设单独的迭代器指向不同的位置。

但是指针通常不是这样工作的,是吗?所以再一次,您的迭代器似乎在欺骗用户:它声称 提供它没有提供的功能。