Генерация произвольно вложенных векторов в C ++ - PullRequest
1 голос
/ 29 января 2020

Я пытаюсь написать функцию для генерации произвольно вложенных векторов и инициализации с заданным значением спецификаций c в C ++. Например, ожидается, что auto test_vector = n_dim_vector_generator<2, long double>(static_cast<long double>(1), 1); создаст объект "test_vector", тип которого std::vector<std::vector<long double>>. Содержимое этого test_vector должно совпадать со следующим кодом.

    std::vector<long double> vector1;
    vector1.push_back(1);
    std::vector<std::vector<long double>> test_vector;
    test_vector.push_back(vector1);

Более сложное использование функции n_dim_vector_generator:

auto test_vector2 = n_dim_vector_generator<15, long double>(static_cast<long double>(2), 3);

В этом случае параметр static_cast<long double>(2) равен как данные в векторах, а число 3 равно как pu sh раз. Итак, содержимое этого test_vector2 должно быть таким же, как и следующий код.

    std::vector<long double> vector1;
    vector1.push_back(static_cast<long double>(2));
    vector1.push_back(static_cast<long double>(2));
    vector1.push_back(static_cast<long double>(2));
    std::vector<std::vector<long double>> vector2;
    vector2.push_back(vector1);
    vector2.push_back(vector1);
    vector2.push_back(vector1);
    std::vector<std::vector<std::vector<long double>>> vector3;
    vector3.push_back(vector2);
    vector3.push_back(vector2);
    vector3.push_back(vector2);
    //...Totally repeat 15 times in order to create test_vector2
    std::vector<...std::vector<long double>> test_vector2;
    test_vector2.push_back(vector14);
    test_vector2.push_back(vector14);
    test_vector2.push_back(vector14);

Подробности реализации функции n_dim_vector_generator следующие.

#include <iostream>
#include <vector>

template <typename T, std::size_t N>
struct n_dim_vector_type;

template <typename T>
struct n_dim_vector_type<T, 0> {
    using type = T;
};

template <typename T, std::size_t N>
struct n_dim_vector_type {
    using type = std::vector<typename n_dim_vector_type<T, N - 1>::type>;
};


template<std::size_t N, typename T>
typename n_dim_vector_type<T,N>::type n_dim_vector_generator(T t, unsigned int);

template <std::size_t N, typename T>
typename n_dim_vector_type<T, N>::type n_dim_vector_generator<N, T>(T input_data, unsigned int push_back_times) {
    if (N == 0)
    {
        return std::move(input_data);
    }
    typename n_dim_vector_type<T, N>::type return_data;
    for (size_t loop_number = 0; loop_number < push_back_times; loop_number++)
    {
        return_data.push_back(n_dim_vector_generator<N - 1, T>(input_data, push_back_times));
    }
    return return_data;
}

В результате я получил ошибка 'return': cannot convert from 'long double' to 'std::vector<std::vector<long double,std::allocator<long double>>,std::allocator<std::vector<long double,std::allocator<long double>>>>' Я знаю, что это вызвано блоком if (N == 0), который является условием завершения для рекурсивной структуры. Однако, если я попытался отредактировать условие завершения в отдельной форме.

template <typename T>
inline T n_dim_vector_generator<0, T>(T input_data, unsigned int push_back_times) {
    return std::move(input_data);
}

template <std::size_t N, typename T>
typename n_dim_vector_type<T, N>::type n_dim_vector_generator<N, T>(T input_data, unsigned int push_back_times) {
    typename n_dim_vector_type<T, N>::type return_data;
    for (size_t loop_number = 0; loop_number < push_back_times; loop_number++)
    {
        return_data.push_back(n_dim_vector_generator<N - 1, T>(input_data, push_back_times));
    }
    return return_data;
}

Произошла ошибка 'n_dim_vector_generator': illegal use of explicit template arguments. Есть ли лучшее решение этой проблемы?

Среда разработки находится в Windows 10 1909 с Microsoft Visual Studio Enterprise 2019 версии 16.4.3

1 Ответ

2 голосов
/ 29 января 2020

Чтобы получить заданное вами c отображение:

auto test_vector = n_dim_vector_generator<2, long double>(2, 3)

на вектор 3x3, заполненный двумя, ваш шаблон может быть немного проще, если вы воспользуетесь этим конструктором vector:

std::vector<std::vector<T>>(COUNT, std::vector<T>(...))

Так как vector можно копировать, это заполнит слоты COUNT другой копией вектора. Итак ...

template <size_t N, typename T>
struct n_dim_vector_generator {
    using type = std::vector<typename n_dim_vector_generator<N-1, T>::type>;
    type operator()(T value, size_t size) {
        return type(size, n_dim_vector_generator<N-1, T>{}(value, size));
    }
};

template <typename T>
struct n_dim_vector_generator<0, T> {
    using type = T;
    type operator()(T value, size_t size) {
        return value;
    }
};

использование:

auto test_vector = n_dim_vector_generator<2, long double>{}(2, 3);

Демо: https://godbolt.org/z/eiDAUG


Для записи, по адресу некоторые замечания из комментариев: C ++ имеет идиоматический c, инициализируемый, непрерывный класс памяти, эквивалентный многомерному массиву C: вложенный std::array:

std::array<std::array<long double, COLUMNS>, ROWS> test_array = { /*...*/ };

for (auto& row : test_array)
    for (auto cell : row)
        std::cout << cell << std::endl;

Если вы хотите уменьшить шаблон для объявления одного, вы можете использовать структуру для этого:

template <typename T, size_t... N>
struct multi_array;

template <typename T, size_t NFirst, size_t... N>
struct multi_array<T, NFirst, N...> {
    using type = std::array<typename multi_array<T, N...>::type, NFirst>;
};

template <typename T, size_t NLast>
struct multi_array<T, NLast> {
    using type = std::array<T, NLast>;
};

template <typename T, size_t... N>
using multi_array_t = typename multi_array<T, N...>::type;

Затем использовать:

multi_array_t<long double, ROWS, COLUMNS> test_array = { /*...*/ };

for (auto& row : test_array)
    for (auto cell : row)
        std::cout << cell << std::endl;

Это расположено в стеке, как массив C. Это, конечно, съест ваше пространство стека для большого массива. Но вы можете сделать диапазон декоратора около std::unique_ptr, чтобы сделать указатель на него немного проще:

template <typename T, size_t... N>
struct dynamic_multi_array : std::unique_ptr<multi_array_t<T, N...>> {
    using std::unique_ptr<multi_array_t<T, N...>>::unique_ptr;
    constexpr typename multi_array_t<T, N...>::value_type& operator [](size_t index) { return (**this)[index]; }
    constexpr const typename multi_array_t<T, N...>::value_type& operator [](size_t index) const { return (**this)[index]; }
    constexpr typename multi_array_t<T, N...>::iterator begin() { return (**this).begin(); }
    constexpr typename multi_array_t<T, N...>::iterator end() { return (**this).end(); }
    constexpr typename multi_array_t<T, N...>::const_iterator begin() const { return (**this).begin(); }
    constexpr typename multi_array_t<T, N...>::const_iterator end() const { return (**this).end(); }
    constexpr typename multi_array_t<T, N...>::const_iterator cbegin() const { return (**this).cbegin(); }
    constexpr typename multi_array_t<T, N...>::const_iterator cend() const { return (**this).cend(); }
    constexpr typename multi_array_t<T, N...>::size_type size() const { return (**this).size(); }
    constexpr bool empty() const { return (**this).empty(); }
    constexpr typename multi_array_t<T, N...>::value_type* data() { return (**this).data(); }
    constexpr const typename multi_array_t<T, N...>::value_type* data() const { return (**this).data(); }
};

(пусть покупатель остерегается, если вы используете эти методы с nullptr)

Тогда вы все еще можете инициализировать выражение new скобками и использовать его как контейнер:

dynamic_multi_array<long double, ROWS, COLUMNS> test_array {
    new multi_array_t<long double, ROWS, COLUMNS> { /* ... */ }
};

for (auto& row : test_array)
    for (auto cell : row)
        std::cout << cell << std::endl;

Демонстрация: https://godbolt.org/z/lUwVE_

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...