template <ForwardIterator I,Sentinel<I> S,class Comp = less<>,class Proj = identity> requires IndirectStrictWeakOrder< Comp,projected<I,Proj>>() I min_element(I first,S last,Comp comp = Comp{},Proj proj = Proj{}); template <ForwardRange Rng,projected<iterator_t<Rng>,Proj>>() safe_iterator_t<Rng> min_element(Rng&& rng,Proj proj = Proj{}) { return __stl2::min_element(__stl2::begin(rng),__stl2::end(rng),__stl2::ref(comp),__stl2::ref(proj)); }
作为参考:range-v3库实现如下:(min_element.hpp)
struct min_element_fn { template<typename I,typename S,typename C = ordered_less,typename P = ident,CONCEPT_REQUIRES_(ForwardIterator<I>() && Sentinel<S,I>() && IndirectRelation<C,P>>())> I operator()(I begin,S end,C pred = C{},P proj = P{}) const; template<typename Rng,typename I = range_iterator_t<Rng>,CONCEPT_REQUIRES_(ForwardRange<Rng>() && IndirectRelation<C,P>>())> range_safe_iterator_t<Rng> operator()(Rng &&rng,P proj = P{}) const { return (*this)(begin(rng),end(rng),std::move(pred),std::move(proj)); } };
现在我试着理解两种方法的区别和推理.
为什么我要按值使用Invocable类型呢?
为什么我不应该为这些类型使用完美转发?
解决方法
>我对标准库规范的阅读是算法可以根据需要多次复制用户函数对象,但是被指定在单个实例上执行所有调用.由于cmcstl2经常根据其他算法实现算法,因此满足该要求的最简单方法是通过reference_wrapper在内部传递函数对象.例如,binary_search调用lower_bound,然后确定由下限表示的元素是否完全匹配.它将reference_wrappers传递给compare_bound并将项目函数对象传递给lower_bound,以便稍后可以调用相同的实例.
>大型和/或可变功能对象可能很少见,但没有理由在标准库中必须支持它们.复制通常很便宜,并且移动几乎总是如此,但通过引用传递“从不”昂贵. cmcstl2最小化两个副本的用户功能对象的移动. (“never”上的空中引号表示,如果别名分析被函数对象引用混淆,那么通过引用传递会给优化器带来更大的负载,增加编译时间并可能在极端情况下生成较差的代码.)
这种推理有一些明显的漏洞.对我来说最重要的是“如果函数对象可能是有用的,那么算法不应该返回它们以保留该状态,std :: for_each也是如此?” cmcstl2的设计基本上违反了编程元素所谓的“有用回归定律”.我们是否应该使标准算法的签名复杂化以返回多达三个函数对象 – 比如比较器和两个投影 – 以容纳0.1%的用例?我认为这里显而易见的答案是“不”,特别是考虑到解决方法很简单:传递一个reference_wrapper.
那么,为什么cmcstl2一般 – 以及标准C的std :: for_each特别是 – 当解决方法类似于传递reference_wrapper时,它们会不会适应大型和/或可变函数对象?似乎cmcstl2的设计者在这里犯了同样的错误,就像LWG让std :: for_each返回它的函数对象一样.