【问题标题】:What's the idiomatic way to generate the numbers from 0 to n-1?生成从 0 到 n-1 的数字的惯用方法是什么?
【发布时间】:2014-02-27 07:01:18
【问题描述】:

在任意类型、数组或向量中生成从 0 到 n-1 的数字时,可接受的 C++ 习语是什么?

换句话说,我该怎么写:

template <typename T> vector<T> generate_integers_upto(size_t n);

template <typename T> T* generate_integers_upto(size_t n);

【问题讨论】:

  • 你会写:std::vector&lt;T&gt; x(n); std::iota(x.begin(), x.end(), T());
  • 你真的需要对象同时存在吗?如果没有,请使用 Boost.Range:template&lt; class T &gt; iterator_range&lt; counting_iterator&lt;T&gt; &gt; generate_integers_upto(size_t n) { return counting_range&lt;T&gt;(0, n); }
  • @SteveJessop Boost::Range 库(以及迭代器)应该被添加到标准库中。我总是怀念标准库中那种高级数字抽象。
  • @Manu343726:我认为有些细节是困难的,比如管理你最终会遇到的各种重载算法,这些算法已经有多种形式,带有可选的谓词等等。

标签: c++ vector idioms


【解决方案1】:

惯用的方法是按值返回。为简单起见,您可以使用 std::iota 填充向量,但这是次要的:

#include <vector>
#include <numeric>

template<typename T>
std::vector<T> generate(std::size_t n)
{
  std::vector<T> v(n);
  std::iota(std::begin(v), std::end(v), T());
  return v;
}

【讨论】:

    【解决方案2】:

    只需按值返回,让编译器决定什么(RVO、移动返回等)更有效:

    template<typename T>
    std::vector<T> generate( std::size_t n , T begin = 0u )
    {
        std::vector<T> result( n );
    
        std::iota( std::begin( result ) , std::end( result ) , begin );
    
        return result;
    }
    

    请注意,默认返回类型是unsigned int。当然你可以改变传递给函数的值来改变返回值,或者显式指定返回类型:

    int main()
    {
        auto sequence = generate<float>( 100 );
    }
    

    此实现基于std::iota() 标准库算法。

    【讨论】:

    • 我相信问题是关于这个函数的惯用实现而不是如何声明它。
    • @AdrianMcCarthy 好的,我已经更新了答案,显示了一个使用 std::iota() 的实现示例。
    • 从签名中删除了右值引用。
    【解决方案3】:

    这取决于你想用这些数字做什么。

    如果您真的想要一个范围,而不是一个容器,那么boost::irange 就足够了。它甚至不需要任何[大量]内存!

    lets you do cool stuff like this:

    #include <iostream>
    #include <boost/range/irange.hpp>
    
    using boost::irange;
    using std::cout;
    
    int main()
    {
        for (auto i : irange(0, 42))
            cout << i << ' ';
    }
    
    // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
    // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
    

    【讨论】:

      【解决方案4】:

      如果您希望该函数为您创建数组,则按值返回std::vector

      如您的第一个示例所做的那样,返回引用要么是无效的(如果向量是一个现在已被销毁的局部变量),要么是奇怪且容易出错(因为现在某处有一个向量需要以某种方式管理)。

      返回一个指针,大概是一个分配的数组,很容易出错,因为没有什么可以确保调用者正确地释放它。

      一种更灵活的替代方法是采用迭代器范围。为两个迭代器重载它可能是有意义的:

      std::vector<int> v(10);       // non-empty vector
      generate(v.begin(), v.end()); // replace existing elements
      

      还有一个迭代器和一个大小:

      std::vector<int> v;             // empty vector
      generate(back_inserter(v), 10); // insert new elements
      

      请注意,C++11 库有一个 std::iota,它的作用类似于第一个版本(并可用于实现其中任何一个),但与第二个不同。

      【讨论】:

        猜你喜欢
        • 2019-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多