Question

What's the acceptable C++ idiom for generating the numbers from 0 to n-1, in an arbitrary type, in an array or a vector?

In other words, how would I write:

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

or

template <typename T> T* generate_integers_upto(size_t n);
Was it helpful?

Solution 3

It rather depends on what you want to do with those numbers.

If you really want a range, not a container, then boost::irange will more than suffice. It doesn't even need any [substantial] memory!

It 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

OTHER TIPS

The idiomatic way would be to return by value. You could use std::iota to fill the vector for simplicity, but this is secondary:

#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;
}

Just return by value and let the compiler decide what (RVO, move return, etc) is more efficient:

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;
}

Note that the default return type is unsigned int. Of course you could change the value passed to the function to change the return value, or specify explicitly the return type:

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

This implementation is based on the std::iota() standard library algorithm.

If you want the function to create the array for you, then return std::vector by value.

Returning a reference, as your first example does, is either invalid (if the vector was a local variable that's now been destroyed) or weird and error-prone (since there's now a vector somewhere that needs managing somehow).

Returning a pointer, presumably to an allocated array, is error-prone since there's nothing to make sure the caller deallocates it correctly.

A more flexible alternative is to take an iterator range. It might make sense to overload it for both a pair of iterators:

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

and an iterator and a size:

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

Note that the C++11 library has a std::iota which acts like the first version (and can be used to implement any of these), but nothing like the second.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top