C ++标准措辞:“通过范围内的所有迭代器”是否意味着顺序性?

时间:2013-02-12 00:51:40

标签: c++ c++11 standards std

This SO question引发了关于std::generate的讨论以及该标准所做的保证。特别是,您可以使用具有内部状态的函数对象并依赖generate(it1, it2, gen)来调用gen(),将结果存储在*it中,再次调用gen(),存储在{{1}中等等,或者它可以从后面开始,例如?

标准(n3337,§25.3.7/ 1)说明了这一点:

  

效果:第一个算法调用函数对象*(it + 1),并通过gen范围内的所有迭代器分配gen的返回值。第二个算法调用函数对象gen,如果[first,last)为正,则通过[first,first + n)范围内的所有迭代器分配gen的返回值,否则它什么都不做。

似乎没有保证排序,特别是因为其他段落具有更强的措辞,例如n效果:将std::for_each应用于取消引用范围{{中的每个迭代器的结果1}},从第一个开始,然后继续f如果我们从字面上理解它,它只能保证从[first,last)开始到last - 1结束 - 不保证订购之间)。

但是: Microsoft'sApache's C++ standard library都提供了文档页面上的示例,这些示例要求评估是连续的。 libc ++(在first)和libstdc ++(在last中)都以这种方式实现它。此外,如果没有此保证,您将失去algorithm的许多潜在申请。

目前的措辞是否意味着顺序性?如果没有,这是委员会成员的疏忽还是故意的?

(我很清楚,没有多少人能够在不仅仅是推测或讨论的情况下为这个问题提供有见地的答案,但在我看来,根据SO指南,这并不会使这个问题“没有建设性”。 )


感谢@juanchopanza指出了这个问题,并引用了我关于bits/stl_algo.h的段落。

2 个答案:

答案 0 :(得分:7)

LWG475的讨论中,std::for_eachstd::transform进行了比较。注意到“transform不保证其函数对象的调用顺序”。所以,是的,委员会意识到标准中缺乏顺序保证。

非顺序行为也没有相反的要求,因此Microsoft和Apache可以自由使用顺序评估。

答案 1 :(得分:5)

标准没有在算法上指定排序的任何地方,您应该假设实现可以利用它来实现并行性。文章n3408讨论了并行化的选项,并指向Thrust库,它既是标准算法的可用并行启用重新实现,也是未来标准化并行化的概念验证。算法

查看Thrust的generate实现,只要迭代器类别是随机访问,它就会在并行循环中调用gen。正如您所观察到的,这与标准一致,因此您不应该假设generate始终是顺序的。 (例如,线程安全的std::rand可以与generate一起使用,并且不需要顺序调用。)

保证顺序调用的唯一算法是numeric中的算法;如果您的代码依赖于顺序调用,则应使用iota代替generate。调整现有的生成器:

template<typename F> struct iota_adapter {
   F f;
   operator typename std::result_of<F()>::type() { return f(); }
   void operator++() {}
};
template<typename F> iota_adapter<F> iota_adapt(F &&f) { return {f}; }

用作:

#include <numeric>
#include <iostream>

int main() {
   int v[5], i = 0;
   std::iota(std::begin(v), std::end(v), iota_adapt([&i]() { return ++i; }));
   for (auto i: v) std::cout << i << '\n';
}