3

I was trying to write a class that will fill a container with random numbers with the type that the container has:

template<typename random_type>
class rand_helper {
private:
    std::mt19937 random_engine;
    std::uniform_int_distribution<int> int_dist;
    std::uniform_real_distribution<double> real_dist;

public:
    rand_helper() = delete;
    rand_helper(random_type left_b, random_type right_b);
    random_type operator()();

};

template<typename random_type>
rand_helper<random_type>::rand_helper(const random_type left_b, const random_type right_b) :random_engine{ std::random_device{}()} {
    if constexpr (std::is_same_v<random_type, double>)
        real_dist(left_b, right_b );
    else
        int_dist( left_b, right_b );
}

template<typename random_type>
random_type rand_helper<random_type>::operator()() {
    if constexpr (std::is_same_v<random_type, double>)
        return real_dist(random_engine);
    else
        return int_dist(random_engine);
}

But here an error occurs somewhere, because when I call std::generate,then I get a lot of errors:

template<typename T,typename vec_type = typename T::value_type>
void fill_contain(T& container,vec_type left_b=vec_type(0), vec_type right_b= vec_type(100)) {
    std::generate(std::begin(container),std::end(container), rand_helper<vec_type>(left_b ,right_b));   
}

I thought it might be because of if constexpr but if just leave:

template<typename random_type>
random_type rand_helper<random_type>::operator()() {
    return int_dist(random_engine);
}

then the same errors are still returned. Here is the list of errors I get:

Error   C2825   '_Urng': must be a class or namespace when followed by '::'
Error   C2510   '_Urng' : left of '::' must be a class / struct / union
Error   C2061   syntax error : identifier 'result_type'
Error   C2065   '_Ty1' : undeclared identifier
Error   C2923   'std::conditional_t' : '_Ty1' is not a valid template type argument for parameter '_Ty2'

The function call goes like this:

 std::vector<int> for_sort;
    fill_contain(for_sort);

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Alpharius
  • 489
  • 5
  • 12
  • 1
    Reopened: This question is clearly not asking how to generate random numbers at compile-time, and none of the answers there explain this compiler error. – chris May 29 '21 at 16:58
  • @chris Oh, you're right, I misread the question. That was a careless closure, thanks for catching it. – cigien May 29 '21 at 17:07

2 Answers2

5

Your code just doesn't compile, regardless of the if constexpr. The reason you may not be getting a compilation error with just the template class is that, well, it's a template, so no actual instance of anything gets compiled. If you add:

template class rand_helper<int>;

which forces an instantiation for a random_type of int, you'll get plenty of compilation error output.

Specifically, you'll be told that you need a pseudo-randomness generator to construct a uniform_int_distribution<int>.


Regardless of the above - you can use something like:

template <typename T>
using uniform_distribution = std::conditional_t<
    std::is_integral_v<T>,
    std::uniform_int_distribution<T>, 
    std::uniform_real_distribution<T>
>;

to only have just one distribution member. And in that case, you might not even need your helper class.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • that is, you can use std:: conditional together with ```std::uniform_int_distribution``` and ```std::uniform_real_distribution```? As far as I remember, it outputs a general type from several, here it can also be done from such elements? – Alpharius May 29 '21 at 17:14
  • @Alpharius: Yes. – einpoklum May 29 '21 at 17:15
  • I can't even imagine how it should work.I thought that it was possible to select a type through the template .it would be very interesting to see an example of what you are talking about – Alpharius May 29 '21 at 17:18
  • Thank you so much,I didn't even think about it.That is, in your opinion, it is better to use your ```uniform_distribution``` and define a random generator somewhere in the lambda function (for example) than to make functors? – Alpharius May 29 '21 at 17:33
  • @Alpharius: I'd go with lambda to start with. – einpoklum May 29 '21 at 17:44
2

To avoid instantiating the std::uniform_real_distribution template class with a non-floating-point type and getting a potentially confusing diagnostic, I'd prefer to use template specializations like this rather than std::conditional_t:

namespace detail
{
    template <typename T, typename AlwaysVoid = void>
    struct uniform_distribution_impl
    {
        static_assert(sizeof(T) == 0, "T must be integral or floating point");
    };

    template <typename T>
    struct uniform_distribution_impl<
        T, std::enable_if_t<std::is_integral_v<T>>>
    {
        using type = std::uniform_int_distribution<T>;
    };

    template <typename T>
    struct uniform_distribution_impl<
        T, std::enable_if_t<std::is_floating_point_v<T>>>
    {
        using type = std::uniform_real_distribution<T>;
    };
}

template <typename T>
using uniform_distribution = typename detail::uniform_distribution_impl<T>::type;
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153