Using c++11's <random> header, what is the correct way to get an integer between 0 and n?

c++, c++11, random

Solution

`uniform_int_distribution` should not be expensive to construct, so creating one every time with new limits should be OK. However, there is a way to use the same object with new limits, but it is cumbersome.

`uniform_int_distribution::operator()` has an overload that takes a `uniform_int_distribution::param_type` object which can specify the new limits to be used, but `param_type` itself is an opaque type, and there's no portable way to construct one except extracting it from an existing `uniform_int_distribution` instance. For instance, the following function can be used to construct a `uniform_int_distribution::param_type`.

std::uniform_int_distribution<>::param_type
    make_param_type(int min, int max)
{
    return std::uniform_int_distribution<>(min, max).param();
}

Pass these to `operator()` and the generated result will be in the specified range.

Live demo

So if you really want to reuse the same `uniform_int_distribution`, create and save multiple instance of `param_type` using the function above, and use these when calling `operator()`.

The answer above is inaccurate, because the standard does specify that the `param_type` can be constructed from the same distribution arguments as those used by the corresponding distribution type's constructor. Thanks to @T.C. for pointing this out.

From §26.5.1.6/9 [rand.req.dist]

For each of the constructors of `D` taking arguments corresponding to parameters of the distribution, `P` shall have a corresponding constructor subject to the same requirements and taking arguments identical in number, type, and default values. `...`

So we don't need to construct the distribution object needlessly only to extract the `param_type`. Instead the `make_param_type` function can be modified to

template <typename Distribution, typename... Args>
typename Distribution::param_type make_param_type(Args&&... args)
{
  return typename Distribution::param_type(std::forward<Args>(args)...);
}

which can be used as

make_param_type<std::uniform_int_distribution<>>(0, 10)

Live demo

Problem

I'm just starting to use C++11's `<random>` header for the first time, but there are still some things that seem a bit mysterious. This question is about the intended, idiomatic, best-practice way to accomplish a very simple task. Currently, in one part of my code I have something like this: ``` std::default_random_engine eng {std::random_device{}()}; std::uniform_int_distribution<> random_up_to_A {0, A}; std::uniform_int_distribution<> random_up_to_B {0, B}; std::uniform_int_distribution<> random_up_to_some_other_constant {0, some_other_constant}; ``` and then when I want an integer between 0 and B I call `random_up_to_B(eng)`. Since this is starting to look a bit silly, I want to implement a function `rnd` such that `rnd(n, eng)` returns a random integer between 0 and n. Something like the following ought to work ``` template <class URNG> int rnd(int n, URNG &eng) { std::uniform_int_distribution<> dist {0, n}; return dist(eng); } ``` but that involves creating a new distribution object every time, and I get the impression that's not the way you're supposed to do it. So my question is, what is the intended, best-practice way to accomplish this simple task, using the abstractions provided by the `<random>` header? I ask because I'm bound to want to do much more complicated things than this later on, and I want to make sure I'm using this system in the right way.

Original source

Related problems