2

I have a class with a std::mersenne_twister_engine member that I want to initialize with a std::seed_seq constructed from a string. Initially I tried this:

class A
{
private:
    std::mt19937_64 rng;
public:
    A(std::string seed) : rng(std::seed_seq(seed.begin(), seed.end())) { }
};

But that doesn't compile, because:

(...) cannot convert argument 1 from 'std::seed_seq' to '_Seed_seq &'

I can get it to work like this:

class B
{
private:
    std::mt19937_64 rng;
public:
    B(std::string seed) {
        std::seed_seq seedSeq(seed.begin(), seed.end());
        rng = std::mt19937_64(seedSeq);
    }
};

But if I understand correctly, the member variable rng will now be constructed twice, so if possible, I'd like to avoid that. So, my main question is: Is it possible to make this work without initializing rng twice?

Before anyone suggests, I've also tried using a separate member function to construct the std::seed_seq object, but the only way I can get it to compile is by returning a const ref like this:

class C
{
private:
    std::mt19937_64 rng;
    const std::seed_seq& makeSeedSeq(std::string seed)
    {
        return std::seed_seq(seed.begin(), seed.end());
    }
public:
    C(std::string seed) : rng(makeSeedSeq(seed)) { }
};

Class C does compile, but when testing with different strings, the results are always the same and always as if the seed was an empty string. I guess this is because makeSeedSeq returns a reference to a local and the result is undefined behavior? This is an aside, but if someone could explain this and perhaps why std::seed_seq was implemented this way, I would very much appreciate it.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
EPLKleijntjens
  • 916
  • 1
  • 8
  • 21
  • For your class `C`, have a look at the first comment of this [question](https://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion-return-statement). Citation: "Please do not return local variables by reference, _ever_. [..] – [fredoverflow](https://stackoverflow.com/users/252000/fredoverflow)" – Michiel uit het Broek Oct 24 '18 at 12:02
  • @Michiel: I was mainly just trying to get something to compile there and I thought a dangling reference might be a problem, hence my guess that the unexpected result was undefined behavior caused by exactly that. However, I also remember reading that the lifetime of a temporary object is extended to the lifetime of a const reference to it. I'm not sure if that applies here or not, but if so, this might be valid code after all. Hence the question mark. ;) – EPLKleijntjens Oct 24 '18 at 13:18

1 Answers1

2

Just add std::seed_seq variable to the class before std::mt19937_64 (variables initialization order is important):

class A
{
private:
    std::seed_seq seed_seq;
    std::mt19937_64 rng;

public:
    A(std::string const& seed)
        : seed_seq(seed.begin(), seed.end())
        , rng(seed_seq)
    {}

    std::uint32_t uniform()
    {
        return std::uniform_int_distribution<std::uint32_t>()(rng);
    }
};

Also I'd suggest to use const& in the constructor to avoid std::string copy constructor execution.

Kozyr
  • 201
  • 1
  • 7