为什么我必须明确指定STLalgorithm函数的范围,即使我想在整个容器上工作?
当使用STL的sort()
或min_element()
函数时,我总是必须明确地指定范围的开始和结束:
void range_example() { std::vector<int> list = {7, 3, 9, 1, 5, 2}; auto found_element = std::min_element(list.begin(), list.end()); std::cout << *found_element << std::endl; }
这是有道理的,如果我打算只在我的容器的一部分工作,但更多的时候我需要function在整个容器上工作。 是否有一个原因,为什么没有一个重载的function,允许这样做:
std::vector<int> list = {7, 3, 9, 1, 5, 2}; auto found_element = std::min_element(list);
有没有办法完成一个我忽略的容器的总范围的函数调用?
编辑:我知道,我可以封装在一个函数本身,但因为这必须做所有function,我想避免,如果有更好的办法。
大多数时候,标准库被devise为提供完成所有需要的任务所需的最小接口,即它试图避免接口膨胀。 当algorithm接受一对迭代器时,您可以在整个容器上操作,但如果algorithm接受容器,则无法在子范围上操作。 所以迭代器对更基础,所以这就是标准库提供的。 便利function通常不包括在内。
不过,你当然不是第一个这样想的人,整个Boost.Range库专门用来处理范围(容器和任意范围)作为一个单一的实体,而不是一对迭代器。
还有一个正式的build议,将Eric Niebler的范围库合并到未来版本的C ++标准库中。
这是因为STLalgorithm是独立于容器的。 迭代器为它们提供了一个统一的工作方式,唯一的限制是这个algorithm需要从这些迭代器中得到什么保证。
例如,如果要对min_element()
进行线性search,则只需要前向迭代器(即只需要支持operator++
)。 所以,你可以编写一个简单的模板化实现,尽pipe容器是如何实现的,但实际上每个容器都可以工作。
你可以重载函数来只取容器,并在它们上应用begin()
和end()
,但是这意味着你还有一个需要记住的接口。
编辑
我想还有其他一些可以提出的论点。 由于STL是关于math美的,强调algorithm与容器是分开的,总是通过迭代器来强化这个概念。
另一方面,就C ++语言而言,Stroustrup的主要目标之一是教育开发人员。 STLalgorithm的全部function来自传递任意迭代器范围的能力,但大多数时候你想要在整个容器上运行。 如果你提供了整个容器的重载,可能会有人争辩说,大量的人将永远不会学习使用范围版本,因为正是这些版本将落入“另一个界面记住”类别。
容器或范围超载尚未完成的实际原因与概念build议有关。
现在,这些algorithm需要大量的模板参数,并对它们进行要求。 如果传递的types与要求不匹配,则可能无法编译或者无法正常工作。
过载几乎总是涉及不同数量的参数。
如果我们在哪里添加容器/范围重载,那么我们要么给它们新的名称(ick),要么修改现有的algorithm,使其成为过载智能的。 一个(迭代器,迭代器,值)重载和一个(范围,值,函数)重载具有相同数量的参数,并且正在调用哪一个可能容易让编译器感到困惑(并且可能发生意外的结果)。
虽然我们可以逐个指定所有现有algorithm的过载约束,然后为范围添加重载,此时代码和要求将会很难。 在将概念添加到语言之后,我们都希望有一套简洁的概念来描述参数应该是什么,还有一个语言特性使实现变得简单和干净。
可能事实上,由于兼容性原因或者你有什么,这些algorithm实际上可能不是现有algorithm的重载,但是即使这样也会更容易解决。
最初,迭代器是足够的,他们从容器中分离algorithm。 可能已经添加了范围,但是对容器进行清晰范围解释的语言机制有点缺乏(例如,decltype是有用的),并不是严格要求的。 从那以后,范围支持一直是需要的,但要做到这一点并不容易,而且(在地平线上)有一个语言扩展,使得它更加简洁和容易。
你可以实现你自己的:
template<class Container> typename Container::iterator min_element(Container& c) { using std::begin; using std::end; return std::min_element(begin(c), end(c)); } std::vector<int> list = {7, 3, 9, 1, 5, 2}; auto found_element = min_element(list);
为了完整性:
template<class Container> typename std::conditional< std::is_const<Container>::value, typename Container::const_iterator, typename Container::iterator>::type min_element(Container& c) { using std::begin; using std::end; return std::min_element(begin(c), end(c)); }
并支持数组:
template<typename T, size_t N> T* min_element(T (&arr)[N]) { return std::min_element(arr, arr + N); }
这是我认为可以使用macros的时代之一。 只要确保计算macros中的expression式没有副作用。
#include <boost/preprocessor/punctuation/comma.hpp> // this is just: #define BOOST_PP_COMMA() , #define RANGE_ARGS( container ) container.begin ( ) BOOST_PP_COMMA() container.end ( ) #define RANGE_ARGS_C( container ) container.cbegin ( ) BOOST_PP_COMMA() container.cend ( ) #define RANGE_ARGS_R( container ) container.rbegin ( ) BOOST_PP_COMMA() container.rend ( ) #define RANGE_ARGS_CR( container ) container.crbegin ( ) BOOST_PP_COMMA() container.crend ( )
这在你的情况下产生:
std::vector<int> list = {7, 3, 9, 1, 5, 2}; auto const found_element = std::min_element( RANGE_ARGS_C(list) );
自己定义这样一个函数是很容易的。 例如
#include <iostream> #include <vector> #include <algorithm> #include <iterator> template <class T> decltype( auto ) min_element( T &c ) { return std::min_element( std::begin( c ), std::end( c ) ); } int main() { int a[] = { 5, 7, 3, 1, 9, 6 }; std::cout << *min_element( a ) << std::endl; std::vector<int> v( std::begin( a ), std::end( a ) ); std::cout << *min_element( v ) << std::endl; }
程序输出是
1 1
我对algorithmstd::sort
和std::reverse
做了这样的build议。 你可以在我的个人论坛中阅读,我支持像我的perosnal网页。 这里
虽然它是用俄语写的,但可以用Bing或Google翻译。