Using a `constexpr static` function defined in a class to initialize a member of the same class

55 views Asked by At

I am a little new to C++ and learning the language. Right now I'm using C++17 and G++.

I'm trying to initialize an array of structs at compile time. I want to explicitly specify the array indexes in which my initial data goes which makes the process much less error prone. For the indexes I'm going to use integer-aliasses, but for the sake of simplicitly I left the alliases out in the example.

I found that this approach could work:

#include <array>

struct Spec
{
    int val;
    double otherVal;
};

constexpr static std::array<Spec, 2> make_init_data()
{
    std::array<Spec, 2> array{};
    array[0] = {1, 3.56};
    array[1] = {1, 3.56};
    return array;
};

constexpr static std::array<Spec, 2> myArray = make_init_data();

This does compile, and myArray is populated.

However, I want myArray to be a class member. So I modified my example to be:

#include <array>

struct Spec
{
    int val;
    double otherVal;
};

class Test
{
    constexpr static std::array<Spec, 2> make_init_data()
    {
        std::array<Spec, 2> array{};
        array[0] = {1, 3.56};
        array[1] = {1, 3.56};
        return array;
    };

    constexpr static std::array<Spec, 2> myArray = make_init_data();
};

Which gives me the following error:

Test.h:19:66: error: 'static constexpr std::array<Spec, 2> Test::make_init_data()' called in a constant expression before its definition is complete
 19 |     constexpr static std::array<Spec, 2> myArray = make_init_data();
    |

I understand that the class definition is not yet complete, and that therefore the constexpr make_init_data cannot be evaluated yet. I can simply put it outside the class definition, but I need many classes doing this. To avoid a lot of cluttering, scrolling and searching, now and in the future my aim is to to have code which is only relevant for functioanlity inside one class to also be inside this class.

Is there a way to use the constexpr so that my array gets populated at compile time while the array is also class member?

(Another way to populate the array at compile time in a similar way without constexpr would also be fine).

3

There are 3 answers

2
Jarod42 On BEST ANSWER

You might do it with lambda directly invoked:

class Test
{
    constexpr static std::array<Spec, 2> myArray = [](){
        std::array<Spec, 2> array{};
        array[0] = {1, 3.56};
        array[1] = {1, 3.56};
        return array;
    }();
};

Demo

4
Eyal Kamitchi On

you can also initialize it outside the class definition.

// Test.hpp
std::array<Spec, 2> Test::myArray = Test::make_init_data();
2
Pepijn Kramer On

I would do it like this a private lambda to calculate the values, and then initialze from that.

#include <array>

struct Spec
{
    int val;
    double otherVal;
};

class Test
{
private:
    static constexpr auto init_array = []
    {
        std::array<Spec, 2> array{};
        array[0] = { 1, 3.56 };
        array[1] = { 1, 3.56 };
        return array;
    };

public:
    static constexpr auto myArray{ init_array() };
};

int main()
{
    static_assert(Test::myArray[0].val == 1);
    return 0;
}