从另一个可变参数模板制作可变参数模板
Making a variadic templete from another variadic template
老实说,我不知道如何开始搜索我试图解决的问题的解决方案。可能已经是解决方案了。所以任务来了
我有一个 class 实际上是带有 2 个参数的模板:
template <typename F, typename S>
class trans {...};
我还有另一个 class 包含这些 "trans" class 的链,就像一个元组(示例):
class holder {
using chain_type = std::tuple<trans<std::string, int>,
trans<int, float>,
trans<float, std::string>,
trans<std::string, json>>;
};
并且可以看出 "trans" 的每个第二个参数与下一个参数相同。链条:
std::string -> int -> float -> std::string -> json.
我想要什么......我想要一些方法来制作这样的链:
template <typename ...Args>
class holder {
using chain_type = trans_chain_create_t<Args...>;
};
holder<std::string, int, float, std::string, json> h;
可能吗?
我对可变参数模板不是很熟悉并且很少使用它们。
是的,有可能:
template< typename F, typename S >
class trans {};
template< typename F, typename S, typename... Tail >
struct create_trans_chain;
template< typename F, typename S, typename... Tail >
using create_trans_chain_t = typename create_trans_chain< F, S, Tail... >::type;
template< typename F, typename S >
struct create_trans_chain< F, S >
{
using type = std::tuple< trans< F, S > >;
};
template< typename F, typename S, typename Next, typename... Tail >
struct create_trans_chain< F, S, Next, Tail... >
{
using type = decltype(std::tuple_cat(
std::declval< create_trans_chain_t< F, S > >(),
std::declval< create_trans_chain_t< S, Next, Tail... > >()));
};
只需展开具有最终特化的递归模板。它是如何工作的,在注释中的代码中进行了描述。看一看:
class json; // as you like that in your given code example... we need to define it
using input = std::tuple< std::string, int, float, std::string, json >;
// First we define a template struct which takes 1 parameter
// No need for a definition as we specialize later
template <typename INPUT >
struct Transform;
// for all inputs which have at minimum 3 template parameters
// inside the std::tuple parameter we use this specialization
template <typename FIRST, typename SECOND, typename NEXT, typename ... TAIL >
struct Transform< std::tuple<FIRST, SECOND, NEXT, TAIL...>>
{
// As we have more than 2 parameters, we continue to transform
// simply by using a recursive "call" to out Transform
// struct
using OUT = decltype( std::tuple_cat(
std::tuple< std::pair< FIRST, SECOND >>(),
std::declval<typename Transform< std::tuple<SECOND, NEXT, TAIL...>>::OUT>()
));
};
// This specialization is used for the last input as
// it has exactly 2 parameters
template <typename FIRST, typename SECOND >
struct Transform< std::tuple<FIRST, SECOND >>
{
using OUT = typename std::tuple<std::pair < FIRST, SECOND>>;
};
using OUT = Transform< input >::OUT;
template < typename T>
void Print()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
Print< Transform< input >::OUT >();
}
没有必要定义你自己的 template <typename F, typename S>
class trans {...};
因为我们有 std::pair
;
对于 Boost.Mp11,这很短(一如既往):
template <typename ...Args>
using trans_chain_create_t =
mp_transform<trans,
mp_pop_back<std::tuple<Args...>>,
mp_pop_front<std::tuple<Args...>>>;
mp_transform
基本上是一个 zip
,我们正在压缩(Args
没有尾巴)和(Args
没有头部)然后成对应用 trans
到那个。
您可以通过添加辅助元函数来拆分以上内容,zip_tail
:
template <template <typename...> class F, typename L>
using zip_tail = mp_transform<F, mp_pop_back<L>, mp_pop_front<L>>;
template <typename ...Args>
using trans_chain_create_t = zip_tail<trans, std::tuple<Args...>>;
来不及玩了?
如果您想要一个非递归解决方案...std::tuple_element
是您的朋友。 (编辑:好吧......显然是非递归的:正如 Andrey Semashev 所指出的,std::tuple_element
本身可能是递归的)。
给定一个已声明(注意:未定义;它仅在 decltype()
中使用)辅助函数,如下所示
template <typename T, std::size_t ... Is>
constexpr auto getChain (std::index_sequence<Is...>)
-> std::tuple<trans<std::tuple_element_t<Is, T>,
std::tuple_element_t<Is+1u, T>>...>;
你的 trans_chain_create_t
简单地(没有递归)变成
template <typename ... Args>
struct trans_chain_create
{ using type = decltype(getChain<std::tuple<Args...>>
(std::make_index_sequence<sizeof...(Args)-1u>{})); };
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
下面是完整的编译(C++14就够了)例子
#include <tuple>
#include <string>
#include <utility>
template <typename, typename>
class trans
{ };
class json
{ };
template <typename T, std::size_t ... Is>
constexpr auto getChain (std::index_sequence<Is...>)
-> std::tuple<trans<std::tuple_element_t<Is, T>,
std::tuple_element_t<Is+1u, T>>...>;
template <typename ... Args>
struct trans_chain_create
{ using type = decltype(getChain<std::tuple<Args...>>
(std::make_index_sequence<sizeof...(Args)-1u>{})); };
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
template <typename ... Args>
struct holder
{ using chain_type = trans_chain_create_t<Args...>; };
holder<std::string, int, float, std::string, json> h;
int main ()
{
using H = holder<std::string, int, float, std::string, json>;
using CT1 = typename H::chain_type;
using CT2 = std::tuple<trans<std::string, int>,
trans<int, float>,
trans<float, std::string>,
trans<std::string, json>>;
static_assert( std::is_same_v<CT1, CT2>, "!" );
}
从 Andrey Semashev 的回答中汲取灵感...非递归(并且没有 std::tuple_element
)版本。
给定一些已声明的函数(不需要定义:仅在 decltype()
内部使用)
template <std::size_t N, std::size_t I, typename, typename>
constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();
template <std::size_t N, std::size_t I, typename T1, typename T2>
constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();
template <std::size_t N, typename ... Ts1, typename ... Ts2,
std::size_t ... Is>
constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
std::index_sequence<Is...>)
-> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));
可以这样写trans_chain_create
(_t
)
template <typename T, typename ... Ts>
struct trans_chain_create
{
using Tpl1 = std::tuple<T, Ts...>;
using Tpl2 = std::tuple<Ts..., T>;
using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;
using type = decltype(getChain<sizeof...(Ts)>
(std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
};
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
下面是完整的编译(C++14就够了)例子
#include <tuple>
#include <string>
#include <utility>
template <typename, typename>
class trans
{ };
class json
{ };
template <std::size_t N, std::size_t I, typename, typename>
constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();
template <std::size_t N, std::size_t I, typename T1, typename T2>
constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();
template <std::size_t N, typename ... Ts1, typename ... Ts2,
std::size_t ... Is>
constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
std::index_sequence<Is...>)
-> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));
template <typename T, typename ... Ts>
struct trans_chain_create
{
using Tpl1 = std::tuple<T, Ts...>;
using Tpl2 = std::tuple<Ts..., T>;
using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;
using type = decltype(getChain<sizeof...(Ts)>
(std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
};
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
template <typename ... Args>
struct holder
{ using chain_type = trans_chain_create_t<Args...>; };
holder<std::string, int, float, std::string, json> h;
int main ()
{
using H = holder<std::string, int, float, std::string, json>;
using CT1 = typename H::chain_type;
using CT2 = std::tuple<trans<std::string, int>,
trans<int, float>,
trans<float, std::string>,
trans<std::string, json>>;
static_assert( std::is_same_v<CT1, CT2>, "!" );
}
老实说,我不知道如何开始搜索我试图解决的问题的解决方案。可能已经是解决方案了。所以任务来了
我有一个 class 实际上是带有 2 个参数的模板:
template <typename F, typename S>
class trans {...};
我还有另一个 class 包含这些 "trans" class 的链,就像一个元组(示例):
class holder {
using chain_type = std::tuple<trans<std::string, int>,
trans<int, float>,
trans<float, std::string>,
trans<std::string, json>>;
};
并且可以看出 "trans" 的每个第二个参数与下一个参数相同。链条:
std::string -> int -> float -> std::string -> json.
我想要什么......我想要一些方法来制作这样的链:
template <typename ...Args>
class holder {
using chain_type = trans_chain_create_t<Args...>;
};
holder<std::string, int, float, std::string, json> h;
可能吗? 我对可变参数模板不是很熟悉并且很少使用它们。
是的,有可能:
template< typename F, typename S >
class trans {};
template< typename F, typename S, typename... Tail >
struct create_trans_chain;
template< typename F, typename S, typename... Tail >
using create_trans_chain_t = typename create_trans_chain< F, S, Tail... >::type;
template< typename F, typename S >
struct create_trans_chain< F, S >
{
using type = std::tuple< trans< F, S > >;
};
template< typename F, typename S, typename Next, typename... Tail >
struct create_trans_chain< F, S, Next, Tail... >
{
using type = decltype(std::tuple_cat(
std::declval< create_trans_chain_t< F, S > >(),
std::declval< create_trans_chain_t< S, Next, Tail... > >()));
};
只需展开具有最终特化的递归模板。它是如何工作的,在注释中的代码中进行了描述。看一看:
class json; // as you like that in your given code example... we need to define it
using input = std::tuple< std::string, int, float, std::string, json >;
// First we define a template struct which takes 1 parameter
// No need for a definition as we specialize later
template <typename INPUT >
struct Transform;
// for all inputs which have at minimum 3 template parameters
// inside the std::tuple parameter we use this specialization
template <typename FIRST, typename SECOND, typename NEXT, typename ... TAIL >
struct Transform< std::tuple<FIRST, SECOND, NEXT, TAIL...>>
{
// As we have more than 2 parameters, we continue to transform
// simply by using a recursive "call" to out Transform
// struct
using OUT = decltype( std::tuple_cat(
std::tuple< std::pair< FIRST, SECOND >>(),
std::declval<typename Transform< std::tuple<SECOND, NEXT, TAIL...>>::OUT>()
));
};
// This specialization is used for the last input as
// it has exactly 2 parameters
template <typename FIRST, typename SECOND >
struct Transform< std::tuple<FIRST, SECOND >>
{
using OUT = typename std::tuple<std::pair < FIRST, SECOND>>;
};
using OUT = Transform< input >::OUT;
template < typename T>
void Print()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
Print< Transform< input >::OUT >();
}
没有必要定义你自己的 template <typename F, typename S>
class trans {...};
因为我们有 std::pair
;
对于 Boost.Mp11,这很短(一如既往):
template <typename ...Args>
using trans_chain_create_t =
mp_transform<trans,
mp_pop_back<std::tuple<Args...>>,
mp_pop_front<std::tuple<Args...>>>;
mp_transform
基本上是一个 zip
,我们正在压缩(Args
没有尾巴)和(Args
没有头部)然后成对应用 trans
到那个。
您可以通过添加辅助元函数来拆分以上内容,zip_tail
:
template <template <typename...> class F, typename L>
using zip_tail = mp_transform<F, mp_pop_back<L>, mp_pop_front<L>>;
template <typename ...Args>
using trans_chain_create_t = zip_tail<trans, std::tuple<Args...>>;
来不及玩了?
如果您想要一个非递归解决方案...std::tuple_element
是您的朋友。 (编辑:好吧......显然是非递归的:正如 Andrey Semashev 所指出的,std::tuple_element
本身可能是递归的)。
给定一个已声明(注意:未定义;它仅在 decltype()
中使用)辅助函数,如下所示
template <typename T, std::size_t ... Is>
constexpr auto getChain (std::index_sequence<Is...>)
-> std::tuple<trans<std::tuple_element_t<Is, T>,
std::tuple_element_t<Is+1u, T>>...>;
你的 trans_chain_create_t
简单地(没有递归)变成
template <typename ... Args>
struct trans_chain_create
{ using type = decltype(getChain<std::tuple<Args...>>
(std::make_index_sequence<sizeof...(Args)-1u>{})); };
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
下面是完整的编译(C++14就够了)例子
#include <tuple>
#include <string>
#include <utility>
template <typename, typename>
class trans
{ };
class json
{ };
template <typename T, std::size_t ... Is>
constexpr auto getChain (std::index_sequence<Is...>)
-> std::tuple<trans<std::tuple_element_t<Is, T>,
std::tuple_element_t<Is+1u, T>>...>;
template <typename ... Args>
struct trans_chain_create
{ using type = decltype(getChain<std::tuple<Args...>>
(std::make_index_sequence<sizeof...(Args)-1u>{})); };
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
template <typename ... Args>
struct holder
{ using chain_type = trans_chain_create_t<Args...>; };
holder<std::string, int, float, std::string, json> h;
int main ()
{
using H = holder<std::string, int, float, std::string, json>;
using CT1 = typename H::chain_type;
using CT2 = std::tuple<trans<std::string, int>,
trans<int, float>,
trans<float, std::string>,
trans<std::string, json>>;
static_assert( std::is_same_v<CT1, CT2>, "!" );
}
从 Andrey Semashev 的回答中汲取灵感...非递归(并且没有 std::tuple_element
)版本。
给定一些已声明的函数(不需要定义:仅在 decltype()
内部使用)
template <std::size_t N, std::size_t I, typename, typename>
constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();
template <std::size_t N, std::size_t I, typename T1, typename T2>
constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();
template <std::size_t N, typename ... Ts1, typename ... Ts2,
std::size_t ... Is>
constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
std::index_sequence<Is...>)
-> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));
可以这样写trans_chain_create
(_t
)
template <typename T, typename ... Ts>
struct trans_chain_create
{
using Tpl1 = std::tuple<T, Ts...>;
using Tpl2 = std::tuple<Ts..., T>;
using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;
using type = decltype(getChain<sizeof...(Ts)>
(std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
};
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
下面是完整的编译(C++14就够了)例子
#include <tuple>
#include <string>
#include <utility>
template <typename, typename>
class trans
{ };
class json
{ };
template <std::size_t N, std::size_t I, typename, typename>
constexpr std::enable_if_t<(I == N), std::tuple<>> filter ();
template <std::size_t N, std::size_t I, typename T1, typename T2>
constexpr std::enable_if_t<(I < N), std::tuple<trans<T1, T2>>> filter ();
template <std::size_t N, typename ... Ts1, typename ... Ts2,
std::size_t ... Is>
constexpr auto getChain (std::tuple<Ts1...>, std::tuple<Ts2...>,
std::index_sequence<Is...>)
-> decltype(std::tuple_cat(filter<N, Is, Ts1, Ts2>()...));
template <typename T, typename ... Ts>
struct trans_chain_create
{
using Tpl1 = std::tuple<T, Ts...>;
using Tpl2 = std::tuple<Ts..., T>;
using IndS = std::make_index_sequence<sizeof...(Ts)+1u>;
using type = decltype(getChain<sizeof...(Ts)>
(std::declval<Tpl1>(), std::declval<Tpl2>(), IndS{}));
};
template <typename ... Args>
using trans_chain_create_t = typename trans_chain_create<Args...>::type;
template <typename ... Args>
struct holder
{ using chain_type = trans_chain_create_t<Args...>; };
holder<std::string, int, float, std::string, json> h;
int main ()
{
using H = holder<std::string, int, float, std::string, json>;
using CT1 = typename H::chain_type;
using CT2 = std::tuple<trans<std::string, int>,
trans<int, float>,
trans<float, std::string>,
trans<std::string, json>>;
static_assert( std::is_same_v<CT1, CT2>, "!" );
}