First of all, defining a C-style variadic function
static bool value_in (T val, T vals, ...)
the comma before the ... is optional.
So your
static bool value_in(T val, T vals...)
define two not-variadic arguments (val and vals) and an unnamed variadic sequence.
How to write declaration that will accept arbitrary number of parameters of the same type?
There are many ways but, IMHO, with drawbacks
A possible way is the use of SFINAE: you can impose that the variadic types are equal to the first type.
The following is a C++17 possible solution that uses template folding
template <typename T, typename ... Ts>
std::enable_if_t<(std::is_same<T, Ts>::value && ...), bool>
value_in (T val, Ts ... vals)
{
const std::unordered_set<T> allowed {val, vals ... };
return allowed.find(val) != allowed.end();
}
You can develop this solution also in C++11/C++14 but is a little more complicated.
Drawback: the Ts... type are deduced and they must be exactly the same T type.
So if you want, by example, a function that accept a list of std::string(), you can't call it with a char const *
value_in(std::string{"abc"}, "123");
because T, std::string, is different from Ts..., char const *, and SFINAE doesn't enable value_in.
You can use std::is_convertible instead of std::is_same but I suggest another way, in two steps.
First of all you need a custom type traits (with using helper) to select the first type from a list
template <typename T, typename ...>
struct firstType
{ using type = T; };
template <typename T, typename ... Ts>
using firstType_t = typename firstType<T, Ts...>::type;
Now you can write a first step value_in() that intercept all values, detect al types (without restriction) and pass they to a second step function as follows
template <typename T, typename ... Ts>
bool value_in (T val, Ts ... vals)
{ return value_in_helper<T, Ts...>(val, vals...); }
The second step function change the all Ts... type in T using firstType
template <typename T, typename ... Ts>
bool value_in_helper (T val, firstType_t<T, Ts> ... vals)
{
const std::unordered_set<T> allowed {val, vals ... };
return allowed.find(val) != allowed.end();
}
This solution is C++11 compatible.
Drawback: you need a second step.
Advantage (IMHO): this solution pass through a second step function that is declared receiving T types so accept also arguments that are convertible to T.
That is: this solution accept also
value_in(std::string{"abc"}, "123");
because there isn't anymore needs that "123" is exactly a std::string; can also be convertible to std::string.