c++classinitializationstatic-membersmost-vexing-parse

How do I initialize a static random generator data member?


I have Random class, and I don`t know how correctly initialize its static data members.

// random.h
#pragma once

#include <random>

class Random
{
private:
    static std::uniform_real_distribution<float> sDistribution_;
    static std::mt19937 sGenerator_;
};
// random.cpp
#include "random.h"

std::mt19937 Random::sGenerator_(std::random_device()); // error here
std::uniform_real_distribution<float> Random::sDistribution_(0.0f, 1.0f);

When I compile this, I get an error:

declaration is not compatible with std::mt19937.

How do I correctly initialize this member?


Solution

  • std::mt19937 Random::sGenerator_(std::random_device());
    

    is an instance of the most vexing parse. You wanted to perform direct initialization, but you're actually:

    declaring Random::sGenerator_ as a function taking a pointer to a function taking no parameters, and returning std::random_device. The Random::sGenerator_ function returns std::mt19937.

    This is what the compiler thinks, and it conflicts with the original definition of Random::sGenerator_, which declares it as a static data member of type std::mt19937.

    The solution is very simple: std::random_device is a type, not a function, so you need to initialize it before calling it, like:

    std::mt19937 Random::sGenerator_{std::random_device{}()};
    // or
    std::mt19937 Random::sGenerator_{std::random_device()()};
    // or
    std::mt19937 Random::sGenerator_(std::random_device{}());
    

    In general, preferring list initialization is considered good practice by some, because it avoids this parsing ambiguity. Just be aware that it works differently when a type has a constructor taking std::initializer_list (not a problem for any of the types we're using).

    Since C++17

    You can also initialize the static data member in the class, like:

    class Random
    {
    private:
        static inline std::uniform_real_distribution<float> sDistribution_{0, 1};
        static inline std::mt19937 sGenerator_{std::random_device{}()};
    };
    

    Note: if all your class does is storing static data members, it should likely just be a namespace, not a class.