能否根据模板参数有条件地添加C++元组元素类型?
Can C++ tuple element types be conditionally added based on template parameters?
我正在尝试根据某些编译时条件有条件地将类型添加到元组模板类型,如下所示:
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>>
{ ... }
我知道这不是有效代码,但从概念上讲,我正在尝试有条件地将类型添加到元组列表中。当条件解析为 void
.
时,我希望跳过该类型
有没有办法做这样的事情?
你的问题有一小部分没有计算:
std::conditional<I % 1 == 0, float, void>:
由于任何整数除以 1 的余数为 0,因此此条件始终为真。
无论这里的实际意图是什么,我都将给出一个示例,其中包含两个条件,可以根据需要简单地扩展以包含其他条件:
#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;
这是利用 std::tuple_cat
对 std::tuple<>
非常满意的事实,我们只需要(ab)使用它来将元组的正确组合粘合在一起。
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 {};}
};
现在
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>
>
好了。
当然在c++20中你可以直接传递types
对象。
让我给出一个替代方案(详见 Sam 的回答):
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>>);
我正在尝试根据某些编译时条件有条件地将类型添加到元组模板类型,如下所示:
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>>
{ ... }
我知道这不是有效代码,但从概念上讲,我正在尝试有条件地将类型添加到元组列表中。当条件解析为 void
.
有没有办法做这样的事情?
你的问题有一小部分没有计算:
std::conditional<I % 1 == 0, float, void>:
由于任何整数除以 1 的余数为 0,因此此条件始终为真。
无论这里的实际意图是什么,我都将给出一个示例,其中包含两个条件,可以根据需要简单地扩展以包含其他条件:
#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;
这是利用 std::tuple_cat
对 std::tuple<>
非常满意的事实,我们只需要(ab)使用它来将元组的正确组合粘合在一起。
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 {};}
};
现在
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>
>
好了。
当然在c++20中你可以直接传递types
对象。
让我给出一个替代方案(详见 Sam 的回答):
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>>);