Reputation: 5597
Suppose I have a following variadic template structure:
template <class... T>
struct Example {};
Now I want to define a template function:
template<class... S>
??? f() {
return Example<???>
}
where the specialization of Example<>
is depend on the template parameter S
of f
.
To be more concrete (and simple), now I just want to return Example<int, ...,int>
, where the number of int
is the size of the parameter pack S
.
How can it be done in modern C++, i.e. C++11/14/17?
More generally, is there a way to at compile time define a function on template parameters?
Upvotes: 3
Views: 211
Reputation: 16156
When doing template meta programming, IMO it always helps to keep the "meta part" and the "non meta part" separate for as long as possible. That way you can first think about the "meta part" as if it were a normal program, operating on types instead of values. So a template becomes a function, taking some type(s) (or for "higher order programming": other templates) as input and returns some type (or for "higher order programming": other templates).
So, first, step back for a second and don't think about templates, metaprogramming, and such. You have a list S
. For each item of S
, you want to call some function, and assemble a list of the returned items. So you need a function that, given one item of the list, returns the item it's mapped to. Let's call this function mapping
. You also need a function which takes said function and applies it to your list, i.e. calls the mapping
on each item and assembles the result list. Let's call that map
.
Now turn this into a meta program:
// 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;
};
Now map
, generalized such that it can use anything like 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...>;
};
Finally, apply to your Example
(which is kind of a list, because it "holds" multiple types) and the concrete mapping
:
template<typename... S>
using MappedExample = map<Example, mapping>::type<S...>;
Now you've got the resulting template, use it in your non-meta program:
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;
}
Output:
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).
Upvotes: 0
Reputation: 38218
You can create a mapping type:
// Maps some type T to the type U.
template <typename T, typename U>
using Map = U;
which you can use as follows:
template<class... S>
Example<Map<S, int>...> f() {
return Example<Map<S, int>...>{};
}
Upvotes: 4