Какой идиоматический способ генерировать числа от 0 до n-1?

0

Какая приемлемая 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);
  • 0
    возможный дубликат того, как «вернуть объект» в C ++
  • 6
    Вы бы написали: std::vector<T> x(n); std::iota(x.begin(), x.end(), T());
Показать ещё 3 комментария
Теги:
vector
idioms
idiomatic

4 ответа

1
Лучший ответ

Это скорее зависит от того, что вы хотите делать с этими цифрами.

Если вам действительно нужен диапазон, а не контейнер, то 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
3

Идиоматическим способом было бы возвращение по значению. Вы можете использовать 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, 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().

  • 0
    Я полагаю, что вопрос заключается в идиоматической реализации этой функции, а не в том, как ее объявить.
  • 0
    @AdrianMcCarthy Хорошо, я обновил ответ, показывая один пример реализации с использованием std::iota() .
Показать ещё 1 комментарий
0

Если вы хотите, чтобы функция создала для вас массив, верните 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

Обратите внимание, что библиотека С++ 11 имеет std::iota которая действует как первая версия (и может быть использована для реализации любого из них), но не похожа на вторую.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню