0

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);
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • possible duplicate of [how to "return an object" in C++](http://stackoverflow.com/questions/3350385/how-to-return-an-object-in-c) – juanchopanza Feb 03 '14 at 17:15
  • 6
    You would write: `std::vector x(n); std::iota(x.begin(), x.end(), T());` – Jerry Coffin Feb 03 '14 at 17:16
  • 1
    Do you actually need the objects to all exist at once? If not then use Boost.Range: `template< class T > iterator_range< counting_iterator > generate_integers_upto(size_t n) { return counting_range(0, n); }` – Steve Jessop Feb 03 '14 at 17:31
  • @SteveJessop the Boost::Range Library (And the iterator too) shouuld be added to the Standard Library. I always miss that kind of high level numeric abstractions on the Standard Library. – Manu343726 Feb 03 '14 at 17:33
  • @Manu343726: I think some of the details are difficult, like managing the various overloads you end up with for algorithms that have multiple forms already with optional predicates or whatever. – Steve Jessop Feb 03 '14 at 17:37

4 Answers4

3

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;
}
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
2

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.

Manu343726
  • 13,969
  • 4
  • 40
  • 75
1

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
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644