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);
}
}
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?
CodePudding user response:
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);
}
}