I want to write a function to return true if T
is one of Ts...
template<class T, class... Ts>
bool is_one_of<T, Ts...>();
For example, is_one_of<int, double, int, float>
returns true
, and is_one_of<int, double, std::string, bool, bool>
returns false
.
My own implementation is
template<class T1, class T2>
bool is_one_of<T1, T2>() {
return std::is_same<T1, T2>;
}
template<class T1, class T2, class... Ts>
bool is_one_of<T1, T2, Ts...>() {
if (std::is_same<T1, T2>) {
return true;
}
else {
return is_one_of<T1, Ts...>();
}
}
This check seems common to me so I wonder if there's already such a function in the standard library.
In your own implementation, one issue is that C++ doesn't allow partial specialization on function templates.
You can use the fold expression (which is introduced in C++17) instead of recursive function call.
template<class T1, class... Ts>
constexpr bool is_one_of() noexcept {
return (std::is_same_v<T1, Ts> || ...);
}
If you are using C++11 where fold expression and std::disjunction
are not available, you can implement is_one_of
like this:
template<class...> struct is_one_of: std::false_type {};
template<class T1, class T2> struct is_one_of<T1, T2>: std::is_same<T1, T2> {};
template<class T1, class T2, class... Ts> struct is_one_of<T1, T2, Ts...>: std::conditional<std::is_same<T1, T2>::value, std::is_same<T1, T2>, is_one_of<T1, Ts...>>::type {};
You can also use std::disjunction
to avoid unnecessary template instantiation:
template <class T0, class... Ts>
constexpr bool is_one_of = std::disjunction_v<std::is_same<T0, Ts>...>;
After a matching type is found, the remaining templates are not instantiated. In contrast, a fold expression instantiates all of them. This can make a significant difference in compile time depending on your use case.
Check if type T is among parameter pack Ts:
template<class T0, class... Ts>
constexpr bool is_one_of = (std::is_same<T0, Ts>{}||...);
template variable.
Alternative:
template<class T0, class... Ts>
constexpr std::integral_constant<bool,(std::is_same<T0, Ts>{}||...)> is_one_of = {};
Which has subtle differences; its type carries its value and it is stateless, instead of being a constexpr bool
value.
The other answers show several correct solutions to solve this specific problem in a clean and concise way. Here is a solution that is not recommended for this specific problem, but demonstrates an alternate technique: In constexpr
functions you can use plain for loops and simple logic in order to compute results at compile time. This allows to get rid of the recursion and the attempted partial template specialization of OP's code.
#include <initializer_list>
#include <type_traits>
template<class T, class... Ts>
constexpr bool is_one_of() {
bool ret = false;
for(bool is_this_one : {std::is_same<T, Ts>::value...}) {
ret |= is_this_one;// alternative style: `if(is_this_one) return true;`
}
return ret;
}
static_assert(is_one_of<int, double, int, float>(), "");
static_assert(!is_one_of<int, double, char, bool, bool>(), "");
Requires at least C++14.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With