3

Risking getting flagged for duplication, I take my chances. Consider the following:

Given the following static const arrays arrA and arrB, arrB depending on arrA.

#include <iostream>
#include <string>
#include <array>

template<int N>
class MyClass {
public:
    static const std::array< int, N> arrA;
    static const std::array< int, N> arrB;
};

template<int N>
std::array<int, N> const MyClass<N>::arrA = []() -> decltype(auto) {
    std::array<int, N> arr;
    for (int i = 0; i < N; i++) {
        arr[i] = 1 + i;
    }
    return arr;
} ();

template<int N>
std::array<int, N> const MyClass<N>::arrB = []() -> decltype(auto) {
    std::array<int, N> arr;
    for (int i = 0; i < N; i++) {
        arr[i] = arrA[i] + 1;
    }
    return arr;
} ();


int main()
{
    constexpr int i = 3;
    std::cout << std::to_string(MyClass<i>::arrB[0]) << std::endl;
}

If I understand correctly, this is a case of unordered initialization of static const member as given in the standard:

1) Unordered dynamic initialization, which applies only to (static/thread-local) class template static data members and variable templates (since C++14) that aren't explicitly specialized. Initialization of such static variables is indeterminately sequenced with respect to all other dynamic initialization except if the program starts a thread before a variable is initialized, in which case its initialization is unsequenced (since C++17). Initialization of such thread-local variables is unsequenced with respect to all other dynamic initialization.

The best answer I could find is here but makes no mention of whether there's a known pattern allowing to perform such initialization in an ordered way. Is that even possible while keeping static const ?

Ideally I'd like the arrays to remain const, otherwise the problem is trivial.

Though this example could be built with constexpr, in a real-world case dynamic initialization is required (I use <random>).

Edit: I find it interesting that no matter the order of declaration or definition in the source, arrB gets initialized before arrA.

Norman Pellet
  • 374
  • 2
  • 9

1 Answers1

0

If you want to guarantee the order of initialisation you have to wrap both arrays in a struct and use the struct's constructor to initialise the static variable. Here is an example of what I mean.

#include <iostream>

template <typename T>
struct A {
    struct B {
        B() : c(0), d(c + 1) {}
        T c;
        T d;
    };
    static B b;

    static T& c() {
        return b.c;
    }

    static T& d() {
        return b.d;
    }
};

template <typename T>
typename A<T>::B A<T>::b{};

int main() {
    std::cout << A<int>::b.c << ", " << A<int>::b.d << std::endl;
    std::cout << A<int>::c() << ", " << A<int>::d() << std::endl;
    return 0;
}

If you do this you might want to provide accessors, as for the example (c() and d()) - you can also play around with the struct being private and with constness for the result of the accessors, so, for example, you can do

template <typename T>
struct A {
    static const T& c() {
        return b.c;
    }

    static T& d() {
        return b.d;
    }

    private:   
    struct B {
        B() : c(0), d(c + 1) {}
        T c;
        T d;
    };
    static B b;
};
Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24