如何在现代 C++ 中基于另一组可变参数模板参数来表示可变参数模板类型?
How to represent a variadic templated type based on another group of variadic template arguments in modern C++?
假设我有以下可变模板结构:
template <class... T>
struct Example {};
现在我要定义一个模板函数:
template<class... S>
??? f() {
return Example<???>
}
其中 Example<>
的特化取决于 f
的模板参数 S
。
更具体(和简单),现在我只想return Example<int, ...,int>
,其中int
的个数就是参数包的大小S
.
如何在现代 C++ 中完成,即 C++11/14/17?
更一般地说,有没有办法在编译时根据模板参数定义函数?
您可以创建映射类型:
// Maps some type T to the type U.
template <typename T, typename U>
using Map = U;
您可以按如下方式使用:
template<class... S>
Example<Map<S, int>...> f() {
return Example<Map<S, int>...>{};
}
在进行模板元编程时,IMO 总是有助于尽可能长时间地保持 "meta part" 和 "non meta part" 分开。这样你就可以先把 "meta part" 看作是一个普通程序,对类型而不是值进行操作。所以一个模板变成了一个函数,将一些类型(或 "higher order programming": 其他模板)作为输入,returns 一些类型(或 "higher order programming": 其他模板)。
所以,首先,退后一步,不要考虑模板、元编程等。您有一个列表 S
。对于 S
的每个项目,您想要调用一些函数,并且 assemble 返回项目的列表。所以你需要一个函数,给定列表中的一个项目,returns 它映射到的项目。我们称这个函数为mapping
。您还需要一个函数,该函数采用上述函数并将其应用于您的列表,即在每个项目上调用 mapping
并在结果列表中调用 assemble。我们称之为 map
.
现在把它变成一个元程序:
// mapping :: TYPE -> TYPE
// ---------------------------------------------------------
// ?? --> int (default "value")
template<typename X> struct mapping {
using type = int;
};
// if instead you want it to be undefined for unknown types:
//template<typename X> struct mapping;
// bool --> double
template<> struct mapping<bool> {
using type = double;
};
现在 map
,广义化后可以使用 mapping
:
// map :: ([T] -> T) -> (T -> T) -> ([T] -> T)
// "List" "Mapping" result "type" (also a "List")
// --------------------------------------------------------
template<template<typename...> class List,
template<typename> class Mapping>
struct map {
template<typename... Elements>
using type = List<typename Mapping<Elements>::type...>;
};
最后,应用于您的 Example
(这是一种列表,因为它 "holds" 多种类型)和具体的 mapping
:
template<typename... S>
using MappedExample = map<Example, mapping>::type<S...>;
现在你已经得到了生成的模板,在你的非元程序中使用它:
template<typename... S>
MappedExample<S...> f() {
return MappedExample<S...>{};
}
int main() {
std::cout
<< typeid(Example<bool,int,char,double>).name()
<< std::endl
<< typeid(decltype(f<bool, int, char, double>())).name()
<< std::endl;
}
输出:
7ExampleIJbicdEE
in the first line, means an Example
with template parameters bool, int, char, double.
7ExampleIJdiiiEE
as second line, means an Example
with template parameters double (mapped from the bool) and 3 int (the default mapping).
假设我有以下可变模板结构:
template <class... T>
struct Example {};
现在我要定义一个模板函数:
template<class... S>
??? f() {
return Example<???>
}
其中 Example<>
的特化取决于 f
的模板参数 S
。
更具体(和简单),现在我只想return Example<int, ...,int>
,其中int
的个数就是参数包的大小S
.
如何在现代 C++ 中完成,即 C++11/14/17?
更一般地说,有没有办法在编译时根据模板参数定义函数?
您可以创建映射类型:
// Maps some type T to the type U.
template <typename T, typename U>
using Map = U;
您可以按如下方式使用:
template<class... S>
Example<Map<S, int>...> f() {
return Example<Map<S, int>...>{};
}
在进行模板元编程时,IMO 总是有助于尽可能长时间地保持 "meta part" 和 "non meta part" 分开。这样你就可以先把 "meta part" 看作是一个普通程序,对类型而不是值进行操作。所以一个模板变成了一个函数,将一些类型(或 "higher order programming": 其他模板)作为输入,returns 一些类型(或 "higher order programming": 其他模板)。
所以,首先,退后一步,不要考虑模板、元编程等。您有一个列表 S
。对于 S
的每个项目,您想要调用一些函数,并且 assemble 返回项目的列表。所以你需要一个函数,给定列表中的一个项目,returns 它映射到的项目。我们称这个函数为mapping
。您还需要一个函数,该函数采用上述函数并将其应用于您的列表,即在每个项目上调用 mapping
并在结果列表中调用 assemble。我们称之为 map
.
现在把它变成一个元程序:
// mapping :: TYPE -> TYPE
// ---------------------------------------------------------
// ?? --> int (default "value")
template<typename X> struct mapping {
using type = int;
};
// if instead you want it to be undefined for unknown types:
//template<typename X> struct mapping;
// bool --> double
template<> struct mapping<bool> {
using type = double;
};
现在 map
,广义化后可以使用 mapping
:
// map :: ([T] -> T) -> (T -> T) -> ([T] -> T)
// "List" "Mapping" result "type" (also a "List")
// --------------------------------------------------------
template<template<typename...> class List,
template<typename> class Mapping>
struct map {
template<typename... Elements>
using type = List<typename Mapping<Elements>::type...>;
};
最后,应用于您的 Example
(这是一种列表,因为它 "holds" 多种类型)和具体的 mapping
:
template<typename... S>
using MappedExample = map<Example, mapping>::type<S...>;
现在你已经得到了生成的模板,在你的非元程序中使用它:
template<typename... S>
MappedExample<S...> f() {
return MappedExample<S...>{};
}
int main() {
std::cout
<< typeid(Example<bool,int,char,double>).name()
<< std::endl
<< typeid(decltype(f<bool, int, char, double>())).name()
<< std::endl;
}
输出:
7ExampleIJbicdEE
in the first line, means anExample
with template parameters bool, int, char, double.
7ExampleIJdiiiEE
as second line, means anExample
with template parameters double (mapped from the bool) and 3 int (the default mapping).