Инициализация массива Stati c в шаблоне функций в C ++ 17 (MSV C 2019) - PullRequest
0 голосов
/ 22 февраля 2020

Я ищу удобный и эффективный способ инициализации массива stati c в шаблоне функции. Например, допустим, у нас есть такая функция:


template <size_t windowSize>
int process_signal(int currentSignal)
{    
    // incorrect: inits only 3 elements; want to set all values to -1
    static std::array<int, windowSize> window = { -1, -1, -1 }; 
    // do something...
}

Таким образом, я могу инициализировать только первые 3 элемента с -1, но не все из них.

Лучшее решение, которое я до сих пор придумал, это использование функции constexpr для инициализации во время компиляции:

template <size_t sz, int value>
constexpr std::array<int, sz> init_array() 
{
    std::array<int, sz> arr{};
    //arr.fill(-1); // will be constexpr since C++20

    // operator [] is constexpr in C++17
    for (int i = 0; i < sz; ++i)
        arr[i] = value;

    return arr;
}


template <size_t windowSize>
int process_signal(int currentSignal)
{    
    // works fine, but extra variable needed
    constexpr static std::array<int, windowSize> init_value = init_array<windowSize, -1>();
    static std::array<int, windowSize> window = init_value;

    // window will be updated...
}

Таким образом, я могу инициализировать массив один раз во время компиляции. Однако для привязки результата функции init_array() требуется дополнительная переменная constexpr (в моем случае init_value). И если я попробую то же самое без него, init_array() вызывается как обычная функция (поэтому инициализация больше не выполняется во время компиляции):

template <size_t windowSize>
int process_signal(int currentSignal)
{    
    // no longer compile-time in MSVC2019
    static std::array<int, windowSize> window = init_array<windowSize, -1>();

    // window will be updated...
}

Можно ли сделать то же самое без дополнительного constexpr переменная? Или, может быть, он просто указывает c для моего компилятора (MSV C 2019)?

1 Ответ

1 голос
/ 22 февраля 2020

Вам не нужна дополнительная переменная, просто напишите:

static auto window = init_array<windowSize, -1>();

Инициализатор все еще является константным выражением и должен оцениваться компилятором во время компиляции.

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

static auto window = []{ constexpr auto x = init_array<windowSize, -1>(); return x; }();

или вы можете поместить ее в отдельную функцию, например в init_array :

template <size_t sz, int value>
constexpr std::array<int, sz> init_array() 
{
    constexpr auto x = []{
        std::array<int, sz> arr{};
        //arr.fill(-1); // will be constexpr since C++20

        // operator [] is constexpr in C++17
        for (int i = 0; i < sz; ++i)
            arr[i] = value;

        return arr;
    }();
    return x;
}

Вы можете сделать функцию гораздо более обобщенной c:

template <std::size_t S, typename T>
constexpr auto init_array(const T& value) 
{
    return std::apply([&](auto... pack){
        return std::array{((void)pack, value)...};
    }, std::array<int, S>{});
}

, используемой как

static auto window = init_array<windowSize>(-1);

или

template <std::size_t S, auto value>
constexpr auto init_array() 
{
    constexpr auto x = std::apply([](auto... pack){
        return std::array{((void)pack, value)...};
    }, std::array<int, S>{});
    return x;
}

используется как

static auto window = init_array<windowSize, -1>;

для принудительной оценки времени компиляции (вплоть до копирования).

...