Something like this, although I can't test it:
template <typename First, typename... Others>
First randompicker(First first, Others ...args) {
const size_t len = sizeof...(args) + 1;
if (rand() / double(RAND_MAX) < 1.0 / len) {
return first;
}
return randompicker(args...);
}
template <typename Only>
Only randompicker(Only only) {
return only;
}
I'm not sure whether the overload there is right -- presumably a parameter pack can be empty, I don't know whether I can still overload for one argument without ambiguity.
Admittedly this uses more random numbers than your example with 3 args, and may be more sensitive to bias from rounding errors. So, you could pick a random number from 0 to len-1 at the start, and then call a recursive function that selects the nth argument out of the parameter pack:
template <typename First, typename... Others>
First select(size_t idx, First first, Others ...args) {
if (idx == 0) return first;
return select(idx-1, args...);
}
template <typename Only>
Only select(size_t, Only only) {
return only;
}
template <typename First, typename... Others>
First randompicker(First first, Others ...args) {
static std::default_random_engine re;
const size_t len = sizeof...(args) + 1;
std::uniform_int_distribution<size_t> range{0, len - 1};
const size_t idx = range(re);
return select(idx, first, args...);
}
In all cases, I have n if/else statements instead of an n-way switch. You might be lucky with the optimizer, or you might be able to "unroll the loop" a bit by having First, Second ... A few parameter args before the variable args.