template parameter incompatible with declaration

I have a function that should generate a random integer or a random floating point value.
For that I want to use concepts.

Since integers and floating points need different distributions, namely std::uniform_int_distribution and std::uniform_real_distribution respectively, I use a separate struct to select the correct type – also with a concept overload.
The "selector" looks like this:

template<std::integral I>
struct distribution_selector {
    using type = std::uniform_int_distribution<I>;
};

template<std::floating_point F>
struct distribution_selector {
    using type = std::uniform_real_distribution<F>;
};

You see that I use using to have a different type selected, depending on whether I use an integer type or a floating point type.

Now my actual function:

template<typename T = float, random_mode quality = random_mode::high_quality>
requires std::integral<T> || std::floating_point<T>
constexpr inline T rand(random& r, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) {
    using distribution = distribution_selector<T>::type;
    if constexpr(quality == random_mode::low_quality) {
        return distribution<T>(min, max)(r.low_quality_engine);
    } else {
        return distribution<T>(min, max)(r.high_quality_engine);
    }
}

demo

I receive the following errors (msvc):

Error (active)  E3244   template constraint not satisfied   Runtime C:\...\rand.h   35      type constraint failed for "float"
atomic constraint evaluates to false
          detected during instantiation of "T raid::rand(raid::random &r, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) [with T=float, quality=raid::random_mode::low_quality]" at line 34
Error (active)  E0519   type "distribution" may not have a template argument list   Runtime C:\...\rand.h   37      detected during instantiation of "T raid::rand(raid::random &r, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) [with T=float, quality=raid::random_mode::low_quality]" at line 34

What am I missing?

>Solution :

Use the concepts in the specialization but not in the declaration of the primary template.
Also, distribution is not a template, its type is determined at its context.

template <typename T>
struct distribution_selector;

template<std::integral I>
struct distribution_selector<I> {
    using type = std::uniform_int_distribution<I>;
};

template<std::floating_point F>
struct distribution_selector<F> {
    using type = std::uniform_real_distribution<F>;
};

template<typename T = float, random_mode quality = random_mode::high_quality>
requires std::integral<T> || std::floating_point<T>
constexpr inline T rand(random& r, T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max()) {
    using distribution = distribution_selector<T>::type;
    if constexpr(quality == random_mode::low_quality) {
        return distribution(min, max)(r.low_quality_engine);
    } else {
        return distribution(min, max)(r.high_quality_engine);
    }
}

Demo

Leave a Reply