Reputation: 13
I was playing around with parameter packs in c++ and here's what I would like to achieve:
I have this 3 structs:
struct Base {
Base(int x) : x(41) {}
int x;
};
struct A : Base {
A() : Base(41) {}
};
struct B : Base {
B() : Base(42) {}
};
And I would like to create a generic function that returns a tuple with instances of the types specified, i.e.
magicFunction<A,B,A,B,A> == std::tuple{A(), B(), A(), B(), A()}
magicFunction<A,B,A> == std::tuple{A(), B(), A()}
I tried to use template specialization to expand the types but was unfortunately unable to do it (I understand why it doesn't compile, just don't know how to fix it :) )
template<typename isEmpty, typename ...T>
struct ExpanderImpl {
};
template<typename Head, typename ... Rest>
struct ExpanderImpl<std::false_type, Head, Rest...> {
static std::tuple<Head, Rest...> getValues() {
if(sizeof...(Rest) > 0) {
return std::tuple_cat(std::tuple{Head()}, ExpanderImpl<std::false_type, Rest...>::getValues());
} else {
return std::tuple_cat(std::tuple{Head()}, ExpanderImpl<std::true_type, Rest...>::getValues());
}
}
};
template<typename ...Empty>
struct ExpanderImpl<std::true_type, Empty...> {
static std::tuple<Empty...> getValues() {
return {};
}
};
template<typename ...T>
struct Expander {
static std::tuple<T...> getValues() {
if(sizeof...(T) > 0) {
return ExpanderImpl<std::false_type, T...>::getValues();
} else {
return ExpanderImpl<std::true_type, T...>::getValues();
}
}
};
Any suggestions on how to fix it? Also, is there a better way of achieving what I want?
The full code can be found here here . Thanks for the help.
Upvotes: 1
Views: 444
Reputation: 217085
Whereas there are simpler ways to create tuple, your way fails as both branches should be instantiated with regular if
.
if constexpr
(C++17) solves that Demo.
Notice that std::tuple{Head()}
is also C++17 construct.
C++11 would be std::tuple<Head>{}
or std::make_tuple(Head())
.
Way to keep your implementation in C++11 would be to get rid of this runtime if
, and use std::conditional
to use either std::true_type
/std::false_type
template<typename Head, typename ... Rest>
struct ExpanderImpl<std::false_type, Head, Rest...> {
static std::tuple<Head, Rest...> getValues() {
return std::tuple_cat(
std::make_tuple(Head()),
ExpanderImpl<typename std::conditional<(sizeof...(Rest) == 0),
std::true_type,
std::false_type>::type,
Rest...>::getValues());
}
};
template<typename ...T>
struct Expander {
static std::tuple<T...> getValues() {
return ExpanderImpl<typename std::conditional<(sizeof...(T) == 0),
std::true_type,
std::false_type>::type,
T...>::getValues();
}
};
Demo.
And you can get rid of is_empty
wth different specialization:
template<typename ...Ts> struct Expander;
// Non empty case
template<typename Head, typename ... Rest>
struct Expander<Head, Rest...> {
static std::tuple<Head, Rest...> getValues() {
return std::tuple_cat(
std::make_tuple(Head()),
Expander<Rest...>::getValues());
}
};
// Empty case
template<>
struct Expander<> {
static std::tuple<> getValues() { return {}; }
};
Demo.
Upvotes: 0
Reputation: 38341
You seem to be looking for
template <typename... T>
using magicFunction = std::tuple<T...>;
int main() {
magicFunction<A,B,A>();
// If no default constructors supplied.
magicFunction<Base,Base,Base>(41, 42, 41);
}
Upvotes: 1
Reputation: 180415
What you are looking for is
template <typename... T> std::tuple<T...> magicFunction()
{
return {};
}
and you would call it like
magicFunction<A,B,A,B,A>();
And it will return a std::tuple<A,B,A,B,A>
. The way this works is return {};
says return a value initialized (T()
/T{}
) return value, and the return value is std::tuple<T...>
which is a tuple of all the template parameters
Upvotes: 1