Reputation: 235
I'm trying to conditionally add types to a tuple template type based on some compile time condition, as follows:
template <typename T>
class Base { ... }
template <int I>
class Derived : public Base<std::tuple<std::conditional<I % 8 == 0, int, void>::type,
std::conditional<I % 4 == 0, double, void>::type,
std::conditional<I % 2 == 0, float, void>::type>>
{ ... }
I understand that this isn't valid code, but conceptually I'm trying to conditionally add types to the tuple's list. I would like the type to be skipped when the conditional resolves to void
.
Is there a way to do something like this?
Upvotes: 14
Views: 985
Reputation: 275405
template <class..Ts>
struct types {
template <template <class...> class Z>
using apply = Z<Ts...>;
template<class...Us>
constexpr types <Ts...,Us...> operator+(types<Us...>)const{return {};}
};
now
template <unsigned int I>
class Derived : public Base<decltype(
std::conditional_t<I & 4 == 0, types<int>, types<>> +
std::conditional_t<I & 2 == 0, types<double>, types<>>{} +
std::conditional_t<I & 1 == 0, types<float>, types<>>{})::template apply<std::tuple>
>
and there you go.
Of course in c++20 you can pass the types
object directly.
Upvotes: 0
Reputation: 26292
Let me give an alternative solution (see Sam's answer for details):
template<bool... Fs, class... Ts>
auto filter_tuple(std::tuple<Ts...>) {
return std::tuple_cat(
std::conditional_t<Fs, std::tuple<Ts>, std::tuple<>>()...);
}
template<typename T>
class Base { };
template<int I>
using Base_t = decltype(filter_tuple<!(I % 4), !(I % 2), !(I % 1)>(
std::tuple<int, double, float>{}));
static_assert(std::is_same_v<Base_t<1>, std::tuple<float>>);
static_assert(std::is_same_v<Base_t<2>, std::tuple<double, float>>);
static_assert(std::is_same_v<Base_t<4>, std::tuple<int, double, float>>);
Upvotes: 0
Reputation: 118340
A small part of your question does not compute:
std::conditional<I % 1 == 0, float, void>:
Since the remainder of any integer when divided by 1 is 0, this conditional will always be true.
Whatever the actual intent was here, I'll just give an example with two conditionals that can be trivially extended to include additional conditionals, as needed:
#include <tuple>
#include <functional>
template <typename T>
class Base {};
template <int I>
class Derived : public Base<
decltype(std::tuple_cat(
std::declval<
std::conditional_t<I % 4 == 0,
std::tuple<int>,
std::tuple<>>>(),
std::declval<
std::conditional_t<I % 2 == 0,
std::tuple<float>,
std::tuple<>>>()
))>
{
};
Derived<4> a;
Base<std::tuple<int, float>> &b=a;
Derived<2> c;
Base<std::tuple<float>> &d=c;
This is taking advantage of the fact that std::tuple_cat
is perfectly happy with std::tuple<>
, and we just need to (ab)use it to glue the right combination of tuples together.
Upvotes: 12