web-dev-qa-db-fra.com

Initialiser un std :: array par algorithme au moment de la compilation

Considérer:

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 est une constante à l'échelle de la classe. Je veux éviter de l'initialiser comme n'importe quelle autre variable globale. Peut-il être fait au moment de la compilation?


Ceci est la classe finale dans son intégralité:

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

Le mélange de code et de variables est inesthétique, mais au moins la formule est juste devant les yeux du lecteur. Toute autre solution impliquait beaucoup de saisie afin d'obtenir la constante et le type définis en classe.

Ou si je change de foyer, je peux simplement retourner le lambda à l'exécution.

36
Vorac

Par souci d'exhaustivité, voici une version qui ne nécessite pas la définition d'une fonction mais utilise à la place un lambda. C++ 17 a introduit la possibilité d'utiliser des lambdas dans des expressions constantes, vous pouvez donc déclarer votre tableau constexpr et utiliser un lambda pour l'initialiser:

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

(Noter la () dans la dernière ligne, qui appelle immédiatement le lambda.)

Si vous n'aimez pas le auto dans la déclaration axis car cela rend plus difficile la lecture du type réel, mais vous ne voulez pas répéter le type à l'intérieur du lambda, vous pouvez à la place faire:

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;
}();
35
Nikos C.

Voici le code compilable complet:

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

Il y a aussi le std::index_sequence tour ( 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>{});
14
metalfox