mike mike - 3 months ago 6
C++ Question

Why there is no uniform_distribution?

We have

uniform_int_distribution
and
uniform_real_distribution
. Wouldnt it be possible to have only
uniform_distribution
but specialize it for float/double and int/... ?

Why standard has not choosen this path?

Answer

AFAIU, the comments above by @user877329 and @revolver_ocelot explain this correctly, and the other answer is completely wrong.

It is wrong to unify the interfaces of uniform_int and uniform_real, not because they are implemented differently (which can be solved via template specialization), but because the interfaces mean different things.

Suppose we unify the interfaces (using a variation of the suggestion in the other answer), like so:

template <typename T>
using uniform_distribution =
    typename std::conditional<
        std::is_integral<T>::value,
        std::uniform_int_distribution<T>,
        std::uniform_real_distribution<T>
    >::type;

Then if we define uniform_distribution<some_type> u(0, 9), the meaning is very different:

  • if some_type is integral, then u will output 9 approximately 1/10ths of the time.

  • if some_type is not, then u will never output 9.

The following code (whose output is true and then false) illustrates this:

#include <random>
#include <iostream>
#include <type_traits>                                         

template <typename T>
using uniform_distribution =
    typename std::conditional<
        std::is_integral<T>::value,
        std::uniform_int_distribution<T>,
        std::uniform_real_distribution<T>
    >::type;

int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());

    {
        uniform_distribution<int> u(0, 9);
        bool over_found = false;
        for(size_t i = 0; i < 99999; ++i)
            over_found = over_found || u(gen) >= 9;
        std::cout << std::boolalpha << over_found << std::endl;
    }

    {
        uniform_distribution<float> u(0, 9);
        bool over_found = false;
        for(size_t i = 0; i < 99999; ++i)
            over_found = over_found || u(gen) >= 9;
        std::cout << std::boolalpha << over_found << std::endl;
    }
}

This code illustrates that writing generic code using this class is dangerous. For example, if you'd write a generic function calculating a histogram of the results in the subranges: [0, 1), [1, 2), ..., [8, 9), the results would be incompatible.


As @revolver_ocelot points out, the standard library's [inclusive-begin, exclusive_end) convention cannot be used for uniform integers (because it would be impossible to specify a uniform integer random number generator generating also the maximum uint value), making this an exceptional signature.