Reputation: 6206
My goal is to write a function that:
Tuple
that represents an arbitrary std::tupleMatch
that represents the type to test forI'm on GCC 10.2, which I can upgrade if needed. I could also switch to Clang. I'm avoiding Boost, but also starting to feel like its inclusion in my project is inevitable.
If C++20 would allow the idx
variable as a compile-time constant, I would be done. However, that didn't make it into the standard:
template<typename Tuple, typename Match>
consteval bool tuple_holds()
{
constexpr std::size_t size = std::tuple_size<Tuple>::value;
constexpr auto all = std::ranges::iota_view{std::size_t{0}, size};
for (const auto idx : all) {
// This doesn't work, as idx is not available as a compile-time constant
using Type = std::tuple_element<idx, Tuple>::type;
if (std::is_same<Type, Match>::value) {
return true;
}
}
return false;
}
using A = std::tuple<float, int>;
int main()
{
static_assert(tuple_holds<A, int>());
static_assert(tuple_holds<A, float>());
}
I suspect the answer lies in SFINAE, but I'm unsure of how to get further.
Upvotes: 3
Views: 1118
Reputation: 60218
Here's another solution that is pretty similar to @HolyBlackCat's answer, but uses std::disjunction
instead of a fold-expression, and can be used the same way you have in your question (it doesn't require ::value
for the usage):
template<typename, typename>
struct tuple_holds;
template<typename ...Ts, typename T>
struct tuple_holds<std::tuple<Ts...>, T>
: std::disjunction<std::is_same<Ts, T>...> {};
Here's a demo.
Upvotes: 2
Reputation: 302922
With Boost.Mp11, this is a short one-line (as always):
template <typename Tuple, typename Match>
static constexpr bool tuple_holds = mp_contains<Tuple, Match>::value;
This doesn't work:
for (const auto idx : all) {
using Type = std::tuple_element<idx, Tuple>::type;
if (std::is_same<Type, Match>::value) {
Because you need idx
to be a constant expression in order to be usable as a template argument, and idx
is not a constant expression. You need to have something like a constexpr
variable for this. This was one of the motivations for the expansion statements language feature - which would've allowed you to write:
template for (constexpr auto idx : all)
Allowing you to use idx
as a template argument - by basically instantiating the body of the for
loop on each iteration. But we don't have that yet.
Upvotes: 4
Reputation: 169008
For a pre-C++17 solution not dependent on fold expressions, you can use a simple recursive template.
// Base: Provide no constant when first argument is not std::tuple<...>
template <typename, typename>
struct tuple_holds {};
// Termination: true
template <typename T, typename... Args>
struct tuple_holds<std::tuple<T, Args...>, T> : std::true_type {};
// Termination: false
template <typename T>
struct tuple_holds<std::tuple<>, T> : std::false_type {};
// Recursive
template <typename T, typename THead, typename... TTail>
struct tuple_holds<std::tuple<THead, TTail...>, T> : tuple_holds<std::tuple<TTail...>, T> {};
Upvotes: 1
Reputation: 96166
You don't need fancy C++20 features to do it.
template <typename, typename>
struct tuple_holds {};
template <typename ...A, typename B>
struct tuple_holds<std::tuple<A...>, B>
: std::bool_constant<(std::is_same_v<A, B> || ...)>
{};
Usage: tuple_holds<std::tuple<A, B>, C>::value
.
Upvotes: 8