C++ 提供操作员+;还是运算符到双向迭代器?

C++ 提供操作员+;还是运算符到双向迭代器?,c++,iterator,operator-overloading,language-lawyer,c++-standard-library,C++,Iterator,Operator Overloading,Language Lawyer,C++ Standard Library,他们没有像这样的奢侈品,因此当有人需要做像这样的操作时,需要依赖于和 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.cbegi

他们没有像这样的奢侈品,因此当有人需要做像这样的操作时,需要依赖于和

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));
  • 使用这些实现有什么缺点吗
  • 如果不是,在C++标准库中有什么不好?

更新

正如他在回答中提到的那样,使用
输入将是一个错误。不过我想展示一下这个想法。无论如何,让我们考虑一个只接受双向迭代器的A。除了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);
}
#包括
#包括//std::iterator\u traits、std::bidirectional\u iterator\u标记、std::next、std::prev
模板
constexpr自动运算符+(BidirIterators),
类型名称std::迭代器特性::差异类型n)
->std::如果启用,则启用<
std::是相同的吗<
std::双向迭代器标记,
类型名称std::迭代器特征::迭代器类别
>,双虹膜
>
{
返回std::next(it,n);
}
模板
constexpr自动运算符-(BidirIterators它,
类型名称std::迭代器特性::差异类型n)
->std::如果启用,则启用<
std::是相同的吗<
std::双向迭代器标记,
类型名称std::迭代器特征::迭代器类别
>,双虹膜
>
{
返回std::prev(it,n);
}

如果不是,在C++标准库中有什么不好?< /P> 没有

如果迭代器提供有编号的递增和递减操作,则表明此类操作是快速的,最好是摊销的O(1)操作。这就是为什么您希望人们拼写出
std::next/prev
:以便编写/阅读代码的人可以完全清楚地看到,所讨论的操作可能很慢。也就是说,即使用户传递的迭代器速度很快,您的算法也会识别出用户可能传递的迭代器速度很慢

在双向迭代器或更小的迭代器上提供这样的运算符就是撒谎。这也是为什么不应该对任意迭代器强制使用此类运算符的原因

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

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

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

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

同意您的后半部分。“它必须是一个双向迭代器”。但是,这难道不是不可避免的吗?关于上半部分,std::next/prev都可能由实现,这表示复杂性:“线性。但是,如果InputIt另外满足LegacyRandomAccessIterator的要求,复杂性是恒定的。”。这意味着,它会处理不同的迭代器情况,还是我遗漏了什么?@JeJo:“这不就是SFINEable吗”当然,但是你发布的代码没有这样做。“这意味着,它会处理不同的迭代器情况,还是我遗漏了什么?”是的,但这不是我的观点<代码>标准::前进/下一步
适用于任何输入计算器。我的观点是,它很大,笨重,而且有一个目的:大声宣布操作可能不是固定时间。通过使用这个函数,你就知道你在做什么<代码>操作员+在设计上有所不同。
#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);
}