Calling the appropriate constructor depending on value_type: integer or float

I have a function that fills a container with random values ​​between min and max using a uniform distribution.

#include <iostream> #include <random> #include <algorithm> #include <vector> template<typename TContainer> void uniform_random(TContainer& container, const typename TContainer::value_type min, const typename TContainer::value_type max) { std::random_device rd; std::mt19937 gen(rd()); // Below line does not work with integers container std::uniform_real_distribution<typename TContainer::value_type> distribution(min, max); auto lambda_norm_dist = [&](){ return distribution(gen); }; std::generate(container.begin(), container.end(), lambda_norm_dist); } int main() { std::vector<float> a(10); uniform_random(a,0,10); for (auto el : a) { std::cout << el << " "; } } 

Replacing std::vector<float> with std::vector<int> does not work, since instead I would have to use std::uniform_int_distribution . Is there a simple and elegant way to choose the right constructor depending on the value_type parameter?

I have tried so far to use std::numeric_limits<typename TContainer::value_type>::is_integer without success.

+5
source share
4 answers

Write a select_distribution meta function that allows you to write this:

 using value_type = typename TContainer::value_type; using distribution_type = typename select_distribution<value_type>::type; distribution_type distribution(min, max); 

where select_distribution is defined as:

 template<typename T, bool = std::is_floating_point<T>::value> struct select_distribution { using type = std::uniform_real_distribution<T>; }; template<typename T> struct select_distribution<T, false> { using type = std::uniform_int_distribution<T>; }; 

Hope this helps.

+6
source

In C ++ 14 (or C ++ 11 with minor changes) you can create an alias of type uniform_distribution as follows:

 template <typename ValueType> using uniform_distribution = std::conditional_t< std::is_floating_point<ValueType>::value, std::uniform_real_distribution<ValueType>, std::uniform_int_distribution<ValueType> >; 

Using:

 uniform_distribution<typename TContainer::value_type> distribution(min, max); 
+7
source

One solution is to use an auxiliary type element and std::enable_if :

 template<class T, class Enable = void> struct uniform_distribution_helper {}; template<class T> struct uniform_distribution_helper<T, typename std::enable_if<std::is_floating_point<T>::value >::type> { using type = std::uniform_real_distribution<T>; }; template<class T> struct uniform_distribution_helper<T, typename std::enable_if<std::is_integral<T>::value >::type> { using type = std::uniform_int_distribution<T>; }; 

Then in your function:

 using uniform_distribution = typename uniform_distribution_helper<typename TContainer::value_type>::type; // Below line does not work with integers container uniform_distribution distribution(min, max); 
+4
source

Try using a feature class. For instance:

 template <typename NumType> struct ValueTraits; template <> struct ValueTraits<int> { using UniformDistributionType = std::uniform_int_distribution<int>; }; 

You can do this based on types (and then you may need quite a few classes) or based on the value of bool (the result is_integer), and then you need a using template.

And then in your class:

 // In the class scope: typedef TContainer::value_type value_type; // And in your constructor: typename ValueTraits<value_type>::UniformDistributionType distribution(min, max); 
0
source

All Articles