0

I'm trying to store the reference of the class "Randomer" initiated in "main" to another class.
What would be best approach, the pointer or reference?

How does the class "Randomer" takes is initial values.

How does this work? operator()()

size_t operator()() {
        return dist_(gen_);
    }

Randomer taken from: https://www.mmbyte.com/

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

someClass{
protected:
Randomer& random;

public:
     someClass(){
     this->random = ....; // <----------------------
     this->random{0, 9};  // this doesn't work.
}

     someClass(Randomer& random){
     this->random = random; // <----------------------
}

}

int main()
{
    Randomer randomer{0, 9}; // how does this work?
    someClass* test =  new someClass(randomer);
    int randomNumber = randomer(); // // how does this work?
    std::cout << "randomNumber" << randomNumber << endl;
    return 0;
}

Result in error:

error: constructor for 'someClass' must explicitly initialize the member 'random' which does not have a default constructor.

The error applies to default constructor as the overloaded constructor.

Edit: If I try reference:

    someClass{
    protected:
    Randomer& random;
    
    public:
         someClass(): : random(new Randomer{0,9}){ // this doesn't work either
    }

         someClass(): random{0}{ // <------ error 1
    }

         someClass(): random{0,9}{ // <------ alternative, error 2
    }
    
         someClass(Randomer& random): random(random){ // all good
         this->random = random; 
    }

    void somefunction(){
    int randomNumber = this->random(); // no complaing
    }
    
    }

int main()
{
    Randomer randomer{0, 9}; // is created here
    someClass* some = new someClass;
    
    someClass* some2 = new someClass(randomer);

}

Error:
1: No matching constructor for initialization of 'Randomer &'
2: Non-const lvalue reference to type 'Randomer' cannot bind to an initializer list temporary

The default constructor needs "Randomer" otherwise error:
Constructor for 'someClass' must explicitly initialize the reference member 'random'.

I would like to have both constructors.

The best bet is to get via reference.

NaturalDemon
  • 934
  • 1
  • 9
  • 21
  • 1
    You need to read a good C++ textbook or other reference on: constructors, initializer lists, brace initialization, and operator overloads. – Botje Sep 11 '20 at 14:11
  • Does this answer your question? [Why should I prefer to use member initialization lists?](https://stackoverflow.com/questions/926752/why-should-i-prefer-to-use-member-initialization-lists) – Yksisarvinen Sep 11 '20 at 14:12
  • The answer there doesn't mention references explicitly, but a reference member, similarly to `const` members, must be initialized from [member initializer list](https://en.cppreference.com/w/cpp/language/constructor) – Yksisarvinen Sep 11 '20 at 14:13

1 Answers1

0

Both constructors of someClass initialize the random member too late. By the time the constructor body is called, all members are either initialized from the member initializer list, or default-initialized. Randomer does not have a default initializer, so that is what the compiler is complaining about.

To fix that problem, write a member initializer list:

someClass(Randomer& random) : random(random) {}

The default constructor has an extra issue: it needs to construct a Randomer& but has no way of freeing the returned object when someClass is destroyed. You could either switch to std::shared_ptr<Randomer> and then let natural reference counting take care of it, or you could take a reference to a static Randomer shared across all someClass objects if that fits your requirements:

class someClass {

    // Note: inline static is a C++17 feature. You will need
    // to split declaration and definition in older C++ versions.
    static inline Randomer global_randomer{0, 9};

public:
    someClass() : randomer{global_randomer} {}
};

As for your other questions:

  • operator()() is simply the function call operator (fully spelled out: operator()), and you're defining it without any parameters, so you add an empty argument list.
  • Brace initialization is new in C++11.
  • Passing Randomer by reference is fine, assuming the "source" remains in scope. If not you might want to use a std::shared_ptr<Randomer> instead, so it is kept alive for as long as a shared_ptr to it exists.
Botje
  • 26,269
  • 3
  • 31
  • 41