C++ has the function std::sample which randomly samples from a range and places the results via an output iterator. My goal is to create a utility that directly returns a single value in the event that we only want to sample a single random element from the container.
#include <iostream>
#include <random>
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include <array>
template<class InputIt, class URBG>
auto singleRandomSample(InputIt t_begin, InputIt t_end, URBG&& rng)
{
using value_type = typename std::iterator_traits<InputIt>::value_type;
std::array<value_type, 1> out;
std::sample(t_begin, t_end, out.begin(), 1, rng);
return out.at(0);
}
int main()
{
std::default_random_engine rng{ std::random_device{}() };
std::unordered_set<char> vowels{ 'a', 'e', 'i', 'o', 'u', 'y' };
for (std::size_t i = 0; i < 10; ++i)
{
auto randomVowel = singleRandomSample(vowels.begin(), vowels.end(), rng);
std::cout << "Random vowel: " << randomVowel << '\n';
}
}
This function creates a temporary container out
to which a single element is added, then returns that element by value. It uses an std::array
because I know I only need room for a single element, although if I decided it was important to be able to use this with non-default-constructible types I could switch to a std::vector
and a std::back_inserter
.
Is it correct to call std::sample
using rng
as an argument directly, or should I be using std::forward(rng)
?
It feels slightly clunky to have an entire temporary container just to store a single value that's immediately returned. Is there another way this functionality could be implemented?
std::sample
. The theory is thatstd::sample
already works, but in the specific case that we only want a single element we can simplify the return type. As you identified my implementation doesn't work with non-default constructible types although it's not hard to modify it to do so. It does work with things like associative containers which I consider nice. \$\endgroup\$