As a contrived example, let's say I want to write a type that can add a new template template parameter to a type that has a variadic template template parameter pack:
namespace
{
template <template <typename...> typename... T>
struct foo {};
template <typename Original, template <typename...> typename T>
class add_to_foo {
template <template <typename...> typename... V>
struct appender {
using type = foo<V..., T>;
explicit appender(foo<V...>) {}
};
template <template <typename...> typename... V>
appender(foo<V...>) -> appender<V...>;
public:
using type = typename decltype(appender(std::declval<Original>()))::type;
};
}
int main() {
using original_foo = foo<std::vector, std::basic_string>;
using new_foo = typename add_to_foo<original_foo, std::deque>::type;
static_assert(std::is_same_v<new_foo, foo<std::vector, std::basic_string, std::deque>>);
return EXIT_SUCCESS;
}
It works, but it's limited in that appender only works with foo rather than a generic template parameter. Let's try to replace foo with a template template parameter:
namespace
{
template <template <typename...> typename... T>
struct foo {};
template <typename Original, template <typename...> typename T>
class add_to_u {
template <template <typename...> typename U, template <typename...> typename... V>
struct appender {
using type = U<V..., T>;
explicit appender(U<V...>) {}
};
template <template <typename...> typename U, template <typename...> typename... V>
appender(U<V...>) -> appender<V...>;
public:
using type = typename decltype(appender(std::declval<Original>()))::type;
};
}
int main() {
using original_foo = foo<std::vector, std::basic_string>;
using new_foo = typename add_to_u<original_foo, std::deque>::type;
static_assert(std::is_same_v<new_foo, foo<std::vector, std::basic_string, std::deque>>);
return EXIT_SUCCESS;
}
This doesn't work, there are lot's of errors, but the main ones are:
<source>:14:25: error: use of template template parameter 'V' requires template arguments
using type = U<V..., T>;
<source>:23:36: error: no viable constructor or deduction guide for deduction of template arguments of 'appender'
using type = typename decltype(appender(std::declval<Original>()))::type;
It looks like the compiler thinks that the template arguments of U need to be complete types rather than template template parameters, this isn't the case when foo was used presumably because the compiler could see that it used template template parameters. So I need to tell the compiler that U is a template template parameter whose parameters are also template template parameters - any idea how?
Related
How can I cascade variadic types? I.e.:
template <typename... T>
using Cascade = ???; // T1<T2<T3<...>>>
Example:
using Vector2D = Cascade<std::vector, std::vector, double>;
static_assert(std::is_same_v<Vector2D, std::vector<std::vector<double>>>);
You cannot have CascadeRight. T1 is not a typename, it is a template, and so are most of the others, but the last one is a typename. You cannot have different parameter kinds (both types and templates) in the same parameter pack. You also cannot have anything after a parameter pack.
You can have CascadeLeft like this:
template <typename K, template <typename...> class ... T>
class CascadeLeft;
template <typename K>
class CascadeLeft<K>
{
using type = K;
};
template <typename K,
template <typename...> class T0,
template <typename...> class... T>
class CascadeLeft<K, T0, T...>
{
using type = typename CascadeLeft<T0<K>, T...>::type;
};
Frankly, std::vector<std::vector<double>> is much more transparent than CascadeLeft<double, std::vector, std::vector>, so I wouldn't bother.
Expanding on the accepted answer with CascadeRight and support for multiple types for the innermost template:
template<template<typename...> typename Head, template<typename...> typename... Tail>
struct CascadeRight {
template<typename... T>
using type = Head<typename CascadeRight<Tail...>::type<T...>>;
};
template<template<typename...> typename Head>
struct CascadeRight<Head> {
template<typename... T>
using type = Head<T...>;
};
template<template<typename...> typename Head, template<typename...> typename... Tail>
struct CascadeLeft {
template<typename... T>
using type = typename CascadeLeft<Tail...>::type<Head<T...>>;
};
template<template<typename...> typename Head>
struct CascadeLeft<Head> {
template<typename... T>
using type = Head<T...>;
};
using T1 = CascadeRight<std::vector, std::map>::type<int, double>;
using T2 = CascadeLeft<std::map, std::vector>::type<int, double>;
Let's say I have a type
template <class T, class U>
class Pass
{
};
I want to make a class that will accept a list of Pass objects, each with arbitrary T and U and make 3 std::tuples.
Something like this:
template<template <typename P1, typename P2> class... T>
class Test
{
public:
Test(T<P1, P2>... passes) {}
std::tuple<T<P1, P2>...> tuple1;
std::tuple<P1...> tuple2;
std::tuple<P2...> tuple3;
};
And then create an instance:
Test t{ Pass<int, float>(), Pass<int, int>(), Pass<std::string, float>() };
Is it possible?
Yes:
template <typename... >
struct Test;
template <typename... T, typename... U>
struct Test<Pass<T, U>...>
{
Test(Pass<T, U>...);
std::tuple<Pass<T, U>...> tuple1;
std::tuple<T...> tuple2;
std::tuple<U...> tuple3;
};
template <typename... T, typename... U>
Test(Pass<T, U>...) -> Test<Pass<T, U>...>;
We need the deduction guide here because class template argument deduction only implicitly considers the primary template's constructors and the primary in this case has no constructors.
Note that this declaration:
template <template <typename P1, typename P2> class... T>
struct X { };
Means the same thing as this declaration:
template <template <typename, typename> class... T>
struct X { };
Which means a parameter pack of binary class templates - not specific types. Such a class template might be instantiated with:
X<Pass, std::pair, std::vector> x;
Consider this:
template <typename Pack, template <typename...> class = std::tuple> struct foo;
template <template <typename...> class P, typename... Ts, template <typename...> class Q>
struct foo<P<Ts...>, Q> {
using type = Q<P<Ts>...>;
};
I've placed the default template in the typedef to be std::tuple to make this compile, but what I actually want is the default template to be P, and I don't know of any syntax that allows this. I kept the typedef simple to illustrate the point, which is trying to get P as the default template. So I came up with the following workaround that seems kind of ugly:
template <typename Pack, template <typename...> class...> struct foo;
template <template <typename...> class P, typename... Ts, template <typename...> class Q>
struct foo<P<Ts...>, Q> {
using type = Q<P<Ts>...>;
};
template <template <typename...> class P, typename... Ts>
struct foo<P<Ts...>> {
using type = P<P<Ts>...>;
};
Is there any better way to do it than this? Perhaps some c++17 syntax that I'm not aware of?
In this case, using (combined with template) is your friend.
You need a type-traits to extract the container in Pack, by example
template <typename>
struct bar;
template <template <typename...> class P, typename ... Ts>
struct bar<P<Ts...>>
{
template <typename ... Us>
using templ_type = P<Us...>;
};
so you can extract the default value, for the second template parameter, from Pack as follows
template <typename Pack,
template <typename...> class = bar<Pack>::template templ_type>
struct foo;
The following is a full compiling example
#include <type_traits>
template <typename ...>
struct baz
{ };
template <typename>
struct bar;
template <template <typename...> class P, typename ... Ts>
struct bar<P<Ts...>>
{
template <typename ... Us>
using templ_type = P<Us...>;
};
template <typename Pack,
template <typename...> class = bar<Pack>::template templ_type>
struct foo;
template <template <typename...> class P, typename... Ts,
template <typename...> class Q>
struct foo<P<Ts...>, Q>
{ using type = Q<P<Ts>...>; };
int main()
{
foo<baz<short, int, long>> f0;
static_assert( std::is_same<decltype(f0)::type,
baz<baz<short>, baz<int>, baz<long>>>{}, "!" );
}
If you're looking for a different default that's an identity, you can create such a thing:
template <class... I> struct foo_id_impl;
template <template <class...> class P, class... Ts>
struct foo_id_impl<P<Ts>...> { using type = P<P<Ts>...>; };
template <class... Id>
using foo_id = typename foo_id_impl<Id...>::type;
template <class Pack, template <class...> class = foo_id>
struct foo;
I'm not sure that's necessarily better than your solution, but maybe if need this in multiple places.
How can I retrieve the template a type was originally instantiated from?
I'd like to do the following:
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = get_template<SomeType>::template type<T>;
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
I know I can achieve this through partial specialization, but this forces me to specialize get_template for every template I want to use it with:
template <typename T>
struct get_template;
template <typename T>
struct get_template<Foo<T>>
{
template <typename X>
using type = Foo<X>;
};
live example
Is there a way around this limitation?
You could do something like that, using a template template parameter (should work for templates with any number of type arguments):
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
Then to get the template:
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
As mentioned by #Yakk in the comment, the above only works for template that only have type arguments. You can specialize for template with specific pattern of type and non-type arguments, e.g.:
// Note: You need the first size_t to avoid ambiguity with the first specialization
template <template <class, size_t, size_t...> class Y, typename A, size_t... Sizes>
struct get_template<Y<A, Sizes...>> {
template <class U, size_t... OSizes>
using type = Y<U, OSizes...>;
};
...but you will not be able to specialize it for arbitrary templates.
DEMO (with Foo and std::pair):
#include <type_traits>
#include <map>
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
static_assert(std::is_same<SomeType, Template<Bar>>::value, "");
static_assert(std::is_same<Foo<int>, Template<int>>::value, "");
using PairIntInt = std::pair<int, int>;
using PairIntDouble = std::pair<int, double>;
template <typename U1, typename U2>
using HopeItIsPair =
typename get_template<PairIntDouble>::type<U1, U2>;
static_assert(std::is_same<PairIntDouble, HopeItIsPair<int, double>>::value, "");
static_assert(std::is_same<PairIntInt, HopeItIsPair<int, int>>::value, "");
Not sure I got the question. Would this work?
#include<type_traits>
#include<utility>
template<typename V, template<typename> class T, typename U>
auto get_template(T<U>) { return T<V>{}; }
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = decltype(get_template<T>(SomeType{}));
int main() {
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
}
You can generalize even more (SomeType could be a template parameter of Template, as an example), but it gives an idea of what the way is.
I have a type trait that checks if a given type is an instance of a given class template:
template <template <typename...> class C, typename T>
struct check_is_instance_of : std::false_type { };
template <template <typename...> class C, typename ...Ts>
struct check_is_instance_of<C, C<Ts...>> : std::true_type { };
template <template <typename...> class C, typename T>
struct is_instance_of : check_is_instance_of<C, std::remove_cv_t<T>> { };
Unfortunately, this does not work for non-type template parameters as they are not "captured" by the variadic template parameters, so
is_instance_of<std::integral_constant, std::true_type>
yields a compile-error. Is there any way to write an implementation of is_instance_of that works for an arbitrary number of type and non-type template parameters?
I don't think there is a clean way to do this unless the non-type arguments are all of the same type and you know which type it is. In that very specific case, function overloading can be used.
In any other case, you end up in a template-argument version of the perfect forwarding problem where you would have to specialize for every type/nontype argument combination.
If you only need to address homogeneous non-template arguments and you can guess the type, the following should work. You can overload instance_of for different types (only int is covered here), but you'd have to explicitly create an instance for each type you want to be able to handle:
// variation for non-type parameters, only for uniform parameters with
// known type.
template <typename V, template <V...> class C, typename T>
struct check_is_instance_of_nontype : std::false_type { };
template <typename V, template <V...> class C, V... Values>
struct check_is_instance_of_nontype<V, C, C<Values...>> : std::true_type { };
// this is as in your example
template <template <typename...> class C, typename T>
struct check_is_instance_of : std::false_type { };
template <template <typename...> class C, typename ...Ts>
struct check_is_instance_of<C, C<Ts...>> : std::true_type { };
template <template <typename...> class C, typename T>
struct is_instance_of : check_is_instance_of<C, std::remove_cv_t<T>> { };
template <template <typename...> class C, typename T>
constexpr bool instance_of()
{
return is_instance_of< C, T>::value;
}
template <template <int...> class C, typename T>
constexpr bool instance_of()
{
return check_is_instance_of_nontype< int, C, T>::value;
}
template< int... >
struct Duck
{
};
template<typename A, typename B>
struct Swallow
{
};
int main() {
typedef Duck<1, 2> SittingDuck;
typedef Swallow< int, int> UnladenSwallow;
std::cout << instance_of< Duck, SittingDuck>() << instance_of< Swallow, UnladenSwallow>();
return 0;
}