Need some explaination at the random facility

I'm struggling with the use of the random facility which the language since C++11 provides. Because I don't understand the semantics of the syntax, I'm shying away from using it - also it's hard to memorize its using because I don't understand the syntax there.
At another thread I found a useful example (thanks goes to @tpb :-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Generate a random input problem in vector<int> form.
// Length is from min_size to max_size; operands are from -99 to 99 (but not 0)
Problem rnd_prob(int min_size, int max_size) {
    using UID = std::uniform_int_distribution<>;
    static auto rng = std::mt19937(std::random_device{}());
    static UID distSize(min_size, max_size);
    static UID distValuesFirst(1, 99);
    static UID distValues(-98, 99);
    int size = distSize(rng);
    Problem prob;
    prob.push_back(distValuesFirst(rng)); // first must be positive
    for (int i = 1; i < size; i++) {
        int n = distValues(rng);
        if (n <= 0) --n;                  // exclude zero
        prob.push_back(n);
    }
    return prob;
}


Especially at these two parts I don't understand the syntax:

using UID = std::uniform_int_distribution<>;
At this above I'm struggling about the empty angled brackets (What does this mean? I know only the use at specifying a type for a templated entity).

And
static auto rng = std::mt19937(std::random_device{}());
Here I'm stumbling over std::random_device{}()
I had never seen this syntax at an other context.

If it's not all too complicated, I would be glad if someone could explain this stuff.
I know only the use at specifying a type for a templated entity
 
using UID = std::uniform_int_distribution<>;

That's what it is: this is how you specify an empty template argument list. It's empty because there's a default template argument. For example:
1
2
3
template <typename T = int> struct A {};
// A<> is the same type as A<int>
static_assert(std::is_same_v<A<>, A<int>>, "is same");


And,
 
static auto rng = std::mt19937(std::random_device{}());

std::random_device is a class. It also happens to provide a function-call operator, so you can call instances of it like a function:
1
2
std::random_device rd{}; // list-initialize a random device named rd
rd(); // okay 


So std::random_device{}() calls a temporary random_device object like a function. The result is an unsigned int. For instance:
1
2
3
4
5
6
7
8
9
10
11
struct my_random_device
{
  // https://xkcd.com/221/
  unsigned int operator()() const noexcept 
  {
    return 4; // chosen by fair dice roll
              // guaranteed to be random
  }
};
// ...
std::cout << (my_random_device{}()) << '\n'; // prints 4 


Consequentially, Melissa O'Neill's famous article is required reading before you use this line of code in real life:
http://www.pcg-random.org/posts/cpp-seeding-surprises.html
The punchline is that this random number generator needs more state than it's given.
Last edited on
Thank you, that helped me a lot!

I've still a question about the curly braces at random_device{}():
These braces, which mean an empty initializer list, are they necessary for letting the compiler recognizing to instantiating an object rather than calling a plain function?
Last edited on
https://en.cppreference.com/w/cpp/language/default_initialization
https://en.cppreference.com/w/cpp/language/value_initialization

For temporary, a plain
my_random_device
would be an error: expected primary-expression

Both
1
2
3
my_random_device{}
// and
my_random_device()

value initialize a temporary object, where T = my_random_device

In order to call the my_random_device::operator() on the temporary, you have to add the function call's ():
1
2
3
my_random_device{}()
// or
my_random_device()()

Topic archived. No new replies allowed.