Инициализировать std :: array алгоритмически во время компиляции

Рассматривать:

static constexpr unsigned num_points{ 7810 };
std::array< double, num_points > axis;

for (int i = 0; i < num_points; ++i)
{
    axis[i] = 180 + 0.1 * i;
}

axis является общеклассовой константой. Я хочу избежать его инициализации, как и любой другой глобальной переменной. Это можно сделать во время компиляции?


Это последний класс во всей своей полноте:

// https://www.nist.gov/pml/atomic-spectroscopy-compendium-basic-ideas-notation-data-and-formulas/atomic-spectroscopy
// https://www.nist.gov/pml/atomic-spectra-database
struct Spectrum
{
    static constexpr unsigned _num_points{ 7810 };
    using Axis = std::array< double, _num_points >;

    static constexpr Axis _x{ [] ()            // wavelength, nm
        {
            Axis a {};
            for( unsigned i = 0; i < _num_points; ++i )
            {
                a[ i ] = 180 + 0.1 * i;
            }
            return a;
        } () };
    Axis _y {};                                // radiance, W·sr−1·m−2
};

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

Или, если я изменю свой очаг, я могу просто вернуть лямбду во время выполнения.

Ответ 1

Для полноты, здесь версия, которая не требует определения функции, а использует лямбду. В С++ 17 появилась возможность использовать лямбда-выражения в константных выражениях, поэтому вы можете объявить свой массив constexpr и использовать лямбда-выражения для его инициализации:

static constexpr auto axis = [] {
    std::array<double, num_points> a{};
    for (int i = 0; i < num_points; ++i) {
        a[i] = 180 + 0.1 * i;
    }
    return a;
}();

(Обратите внимание на () в последней строке, которая сразу вызывает лямбду.)

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

static constexpr std::array<double, num_points> axis = [] {
    auto a = decltype(axis){};
    for (int i = 0; i < num_points; ++i) {
        a[i] = 180 + 0.1 * i;
    }
    return a;
}();

Ответ 2

Вот полный скомпилированный код:

#include <array>

template<int num_points>
static constexpr std::array<double, num_points> init_axis() {
    std::array<double, num_points> a{};
    for(int i = 0; i < num_points; ++i) 
    {
        a[i] = 180 + 0.1 * i;
    }
    return a;
};

struct Z {
    static constexpr int num_points = 10;
    static constexpr auto axis = init_axis<num_points>();
};

Ответ 3

Также std::index_sequence трюк std::index_sequence (Wandbox example):

template <unsigned... i>
static constexpr auto init_axis(std::integer_sequence<unsigned, i...>) {
   return std::array{(180 + 0.1 * i)...};
};

static constexpr auto axis = init_axis(std::make_integer_sequence<unsigned, num_points>{});