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
.