C ++ инициализирует / заполняет статический член std :: array элементами до определенного значения - PullRequest
1 голос
/ 20 июня 2019

Мне интересно, есть ли правильный способ сделать это.

Учитывая пример:

struct Test {
    static std::array<unsigned, 123> data;
};

std::array<unsigned, 123> Test::data = {};

Если я хочу установить все элементы в data вкакое-то значение, например 5.

Я мог бы сделать что-то вроде этого:

struct Test {
    static std::array<unsigned, 123> data;
private:
    static decltype(data) init_data_arr() {
        decltype(data) arr = {};
        arr.fill(5);
        return arr;
    }
};

std::array<unsigned, 123> Test::data = Test::init_data_arr();

Но тогда я бы выделил дополнительную память для массива и сделал бы копию для всего этого,что не кажется идеальным.

Еще один менее ресурсоемкий вариант будет выглядеть примерно так:

struct Test {
    static bool is_init;
    static std::array<unsigned, 123> data;

    Test() {
        if (!is_init) {
            is_init = true;
            data.fill(5);
        }
    }
};

bool Test::is_init = false;
std::array<unsigned, 123> Test::data = {};

Но теперь у меня есть дополнительные издержки при создании моей структуры Test.

Теперь я знаю, что вы можете инициализировать массивы pod следующим образом: std::array<int, 3> a = {1, 2, 3};, но для массивов размером N это быстро станет ужасно поддерживать.

Каждое решение, с которым я сталкиваюсь, просто использует функцию-член fillв классе массива, но всегда обрабатывает массивы так, как будто они не являются статическими переменными-членами, или инициализирует их в main, и то и другое не очень помогает моей ситуации.

Редактировать: я создал этот служебный заголовок, которыйобобщает 2-й опТион.#pragma Once

#include <utility>
#include <iterator>
#include <algorithm>
#include <type_traits>

namespace Utils {

    template<typename Container>
    using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<Container&>()))>;

    template<class Container>
    static Container create_filled_container(const element_type_t<Container>& value) {
        Container container = {};
        std::fill(container.begin(), container.end(), std::move(value));
        return container;
    }

}

Возможно, это можно улучшить еще больше, но, похоже, это работает.Использование выглядит следующим образом:

std::array<unsigned, 123> Test::data = Utils::create_filled_container<decltype(data)>(456);

, который устанавливает все элементы в соответствии с тем, что вы укажете.

Edit2: Итак, я провел еще несколько тестов и похоже на вызов функции для заполнения массива против вручнуювыполнение этого не всегда приводит к одной и той же сборке.

Я установил демо здесь !

Edit3: изменил некоторые вещи, чтобы они могли быть constexpr, вотего текущая форма:

template<class Container>
static constexpr Container create_filled_container(const element_type_t<Container>& value) {
    Container container = {};

    for (auto it = container.begin(); it != container.end(); ++it)
        *it = value;

    return container;
}

, которая теперь производит ту же сборку.

1 Ответ

1 голос
/ 20 июня 2019

Инициализируйте его с помощью функции constexpr или лямбды. Код времени выполнения не генерируется.

#include <array>
#include <iostream>

template <unsigned len, unsigned val>
constexpr std::array<unsigned, len> initialize_array()
{
    std::array<unsigned, len> ret{};
    for (int i = 0; i < len; i++)
        ret[i] = val;
    return ret;
}

struct Test {
    constexpr static std::array<unsigned, 123> data{ initialize_array<123,5>() };
};

int main() {
    std::cout << Test::data[4] << "\n";
}
...