Search code examples
c++initializationc++17compile-timestdarray

Initialize an std::array algorithmically at compile time


Consider:

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 is a class-wide constant. I want to avoid initializing it like any other global variable. Can it be done at compile time?


This is the final class in its entirety:

// 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
};

The mixing of code and variables is unsightly, but at least the formula is right in front of the reader's eyes. Any other solution involved a lot of typing in order to get the in-class defined constant and type.

Or if I change my heart, I can simply return the lambda at runtime.


Solution

  • For completeness' sake, here's a version that does not require the definition of a function but instead uses a lambda. C++17 introduced the ability of using lambdas in constant expressions, so you can declare your array constexpr and use a lambda to initialize it:

    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;
    }();
    

    (Note the () in the last line, which calls the lambda right away.)

    If you don't like the auto in the axis declaration because it makes it harder to read the actual type, but you don't want to repeat the type inside the lambda, you can instead do:

    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;
    }();