Какова приемлемая идиома C ++ для генерации чисел от 0 до n-1, в произвольном типе, в массиве или в векторе?
Другими словами, как бы я написал:
template <typename T> vector<T> generate_integers_upto(size_t n);
или же
template <typename T> T* generate_integers_upto(size_t n);
Это скорее зависит от того, что вы хотите сделать с этими числами.
если ты действительно хотите диапазон, а не контейнер, тогда boost::irange
будет более чем достаточно. Ему даже не нужна [существенная] память!
Это позволяет делать такие классные вещи, как это:
#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
Идиоматическим способом было бы возвращение по значению. Вы могли бы использовать 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;
}
Просто верните значение и позвольте компилятору решить, что (RVO, move return и т. Д.) Более эффективно:
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()
алгоритм стандартной библиотеки.
Если вы хотите, чтобы функция создала для вас массив, верните 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
которая действует как первая версия (и может использоваться для реализации любой из них), но не похожа на вторую.