Reputation: 132006
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);
Upvotes: 0
Views: 970
Reputation: 385295
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
Upvotes: 1
Reputation: 254651
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.
Upvotes: 0
Reputation: 227498
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;
}
Upvotes: 3
Reputation: 14174
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.
Upvotes: 2