提供运算符+或运算符到双向迭代器有什么缺点吗?
Is there any drawbacks in providing operator+ or operator- to bidirectional iterators?
双向迭代器没有像随机访问迭代器那样的奢侈品,因此需要依赖于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));
但是,我们可以使用上述std::next
和std::prev
来实现这些operator+
和operator-
。
#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
}
- 使用这些实现有什么缺点吗?
- 如果没有,在C++标准库中拥有不是很好吗?
更新:
正如@Nicol波拉斯在他的回答中提到的那样,使用InputIt
将是一个错误。不过我想证明一下这个想法。无论如何,让我们考虑一个SFINE解决方案,它只接受双向迭代器。除了尼科尔提到的之外,还有其他问题吗?
#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
无效。它必须是一个双向迭代器。
但还有更多。可以复制前向迭代器,各种副本被视为范围内的独立位置。纯输入迭代器不是这种情况;如果复制它们,则假定递增一个副本会使该迭代器的所有其他副本无效。因此,用户在尝试使用它时必须知道这一点,并注意不要假设单独的迭代器指向不同的位置。
但这不是指针通常的工作方式,是吗?所以再一次,你的迭代器似乎在对用户撒谎:它声称提供它没有的功能。
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 如果我真的真的想从 STL 容器继承,并且我继承构造函数并删除新运算符,会发生什么?
- 这里的 = 运算符有什么用法?
- unique_ptr < 0 或小于运算符做什么?
- "operator()"在重载运算符方法中是什么意思,在priority_queue(STL)中用作C++中的比较器?
- 是什么让一些命名函数/运算符与众不同?
- C++,()运算符重载,它的工作是什么
- 提供运算符+或运算符到双向迭代器有什么缺点吗?
- std::set<Key,Compare,Allocator>::find() 函数使用"<"运算符而不是"=="运算符背后的直觉是什么?
- 有什么理由不扩展 std::set 以添加下标运算符吗?
- 在C++中,运算符 sizeof 返回什么数据类型?
- 运算符++();调用和++(*this)有什么区别?
- 第二个常量在运算符函数中做什么?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 当值传递给C++中的运算符重载函数时会发生什么
- 除了调用全局删除运算符之外,删除一个void指针还能做什么呢
- c++运算符重载-我实际返回的操作数类型是什么
- 这个typedef和转换运算符语法是什么意思
- 当正斜杠运算符应用于C++中的字符串类型时,它会做什么
- 运算符和返回类型是什么意思?