How to alias a nested template class with variadic parameter packs - c++

Is there any way one can alias a nested template class with a using keyword? Something like this
template <typename... Types>
struct Something {
template <typename... TypesTwo>
struct Another {};
};
template <typename... Types>
template <typename... TypesTwo>
using Something_t = typename Something<Types...>::template Another<TypesTwo...>;
int main() {
Something_t<int><double>{};
return 0;
}
This answer template template alias to a nested template? shows a way to do that but that will no longer work if both the parameter packs are variadic, as the compiler will not know where to start and where to end the type lists.

Not exactly what you asked but... if you can wrap your variadic type lists as arguments of tuples (or similar classes)...
#include <tuple>
template <typename ... Types>
struct Something
{
template <typename ... TypesTwo>
struct Another {};
};
template <typename, typename>
struct variadicWrapper;
template <typename ... Ts1, typename ... Ts2>
struct variadicWrapper<std::tuple<Ts1...>, std::tuple<Ts2...>>
{ using type = typename Something<Ts1...>::template Another<Ts2...>; };
template <typename T1, typename T2>
using Something_t = typename variadicWrapper<T1, T2>::type;
int main()
{
Something_t<std::tuple<int>, std::tuple<double>>{};
}

Not a standalone answer, but an addition to max66's answer:
You could have tried this:
template<typename ... TT, typename ... TTT>
using Alias = typename Something<TT...>::Another<TTT...>;
Looks really nice at first, doesn't it?
Problem then, however will already be with one single template parameter:
Alias<int> a;
Which one is it now? Something<int>::Another<> or Something<>::Another<int>? And if you have more parameters, how to distribute? No chance to get a meaningful solution. So no, you can't do that directly, you have to help yourself with tricks such as max66 proposed...

Related

Short way to constrain template parameter without boiler plate code for a struct

consider this example:
#include <iostream>
template <typename T, std::size_t I>
struct Foo
{
};
template <typename T>
struct specializes_foo : std::false_type {};
template <typename T, std::size_t I>
struct specializes_foo<Foo<T, I>> : std::true_type {};
template <typename T>
concept foo = specializes_foo<T>::value;
int main()
{
std::cout << foo<int> << "\n";
std::cout << foo<Foo<int,3>> << "\n";
}
is there a shorter way in C++20? I know that you could do a concept for "specializes template", e.g.:
// checks if a type T specializes a given template class template_class
// note: This only works with typename template parameters.
// e.g.: specializes<std::array<int, 3>, std::array> will yield a compilation error.
// workaround: add a specialization for std::array.
namespace specializes_impl
{
template <typename T, template <typename...> typename>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> typename template_class>
struct is_specialization_of<template_class<Args...>, template_class> : std::true_type {};
}
template <typename T, template <typename...> typename template_class>
inline constexpr bool is_specialization_of_v = specializes_impl::is_specialization_of<T,template_class>::value;
template <typename T, template <typename...> typename template_class>
using is_specialization_of_t = specializes_impl::is_specialization_of<T,template_class>::type;
template<typename T, template <typename...> typename template_class>
concept specializes = is_specialization_of_v<T, template_class>;
However, this fails with non-type template parameters such as "size_t". Is there a way so I could, for example just write:
void test(Foo auto xy);
I know that since C++20 there came a couple of other ways to constrain template parameters of a function, however, is there a short way to say "I dont care how it specializes the struct as long as it does"?
Nope.
There's no way to write a generic is_specialization_of that works for both templates with all type parameters and stuff like std::array (i.e. your Foo), because there's no way to write a template that takes a parameter of any kind.
There's a proposal for a language feature that would allow that (P1985), but it's still just a proposal and it won't be in C++23. But it directly allows for a solution for this. Likewise, the reflection proposal (P1240) would allow for a way to do this (except that you'd have to spell the concept Specializes<^Foo> instead of Specializes<Foo>).
Until one of these things happens, you're either writing a bespoke concept for your template, or rewriting your template to only take type parameters.

Is it possible to create a template template parameter list?

I was wondering whether it's possible to make a list containing template template parameters, like
template <
template <typename...> class TTP0,
template <typename...> class... TTPs
>
struct TTP_List : TTP_List<TTPs...> {};
A problem I encountered is that I did not know a good way to access the elements of the list, that is, TTP0. I would like to use type aliases, typedef or using. I however know this is not possible, because the template template parameters are not types and must therefor not be treated as such.
A approach I could imagine working is making explicit structs to read the data and make it use specializations, such as:
template <template <typename...> class>
struct container_TTPs;
template <template <typename...> class TTP>
struct container_TTPs<std::vector> {};
However, this approach seems very explicit. Is there a way to accomplish this recognition without this use of template specialization?
Feel free to ask if I need to elaborate.
EDIT: For example, I want to be able to use certain expressions for every held type, such as TestClass<TTP_List<std::vector, std::list>>::value, where TestClass uses the std::vector and the std::list, without requiring a variadic template within TestClass, so that multiple TTP_Lists can be passed.
I don't understand what do you mean with "access the elements of the list".
It seems to me that you should give us an example of what do you want, concretely, do accessing elements of the list.
Anyway, as you can use using to "access" (?) typenames
template <typename T0, typename ... Ts>
struct foo
{
using type = T0;
};
you can use a template using to "access" (?) a template-template parameter as follows
template <template <typename...> class T0,
template <typename...> class ... Ts>
struct foo
{
template <typename ... As>
using templ_type = T0<As...>;
};
and you can use it in this way
// vi is a std::vector<int>
foo<std::vector, std::set, std::map>::templ_type<int> vi { 0, 1, 2 };
The problem (a problem) is that a template parameter variadic list (isn't important if of typenames, of values or of template-templates) must be in last position.
So
template <typename ... Ts, typename T0>
struct foo
{ };
is wrong, because Ts... must be in last position, and
template <typename T0, typename ... Ts>
struct foo
{ };
is correct.
With template-template parameters,
template <template <typename ...> class ... Ts,
template <typename ...> class T0>
struct foo
{ };
is wrong where
template <template <typename ...> class T0,
template <typename ...> class ... Ts>
struct foo
{ };
is correct.

alias for nested template type

I have the following code:
template <template <typename, typename...> typename trait_t, typename arg_t>
struct BindFirst
{
template <typename... arg_ts>
using result_t = trait_t<arg_t, arg_ts...>;
};
#define BIND_FIRST(trait_t, arg_t) BindFirst<trait_t, arg_t>::template result_t
you can use it to bind the first argument of a trait like this:
BIND_FIRST(std::is_same, double)
The result is equivalent to:
template <typename T>
struct IsInt : std::is_same<double, T> { };
The difference is, that you can use it inline. For example like this:
using result_t = find_t<type_list, BIND_FIRST(std::is_same, double)>;
This works but i like to avoid the define. I tried to use an alias. But I have no idea how to apply it. Is there any way to replace the define?
You can use templates with using to create an alias template, just like you did for result_t.
template <typename... Args>
using IsDouble = BindFirst<std::is_same, double>::template result_t<Args...>;
You can limit the Args... to a single type T as well, it doesn't have to be variadic.
Edit: If your goal is to reduce boilerplate, you may want to opt for something like this
template <typename T, typename U>
using IsSameAs = std::is_same<T, U>;
template <typename T>
using IsDouble = IsSameAs<double, T>;

What can you do with templates with zero template parameters?

I learned some time ago that you can create templates with zero parameters. While it is not possible to create them directly, you can use member templates
template<typename ...T>
struct Maker {
template<T...>
struct HasNParams { };
};
Maker<>::HasNParams<> hnp;
I wonder whether this is intended to be well-formed and what you can do with these beasts. Can you pass them as template arguments, and create explicit specializations (I guess the only scenario is for the empty case then)?
At the risk of sounding obvious, ending recursive instantiation.
template<typename Arg, typename ...T>
struct Maker : public Maker<T...>
{
template<T...>
struct HasNmin1Params { };
};
The point here is that the actual argument list to Maker isn't empty, but we only use N-1 arguments in HasNminOneParams.
Consider the following class template:
template <typename... > struct typelist { };
This is the metaprogramming equivalent of a container. And in the same way that it is useful to have an empty vector or map, it is useful to have an empty typelist. That is, something of type typelist<>. Here are two example use-cases for such a construct.
It could be the termination condition for type recursion:
void foo(typelist<> ) { }
template <typename T, typename... Ts>
void foo(typelist<T, Ts...> ) {
bar<T>();
foo(typelist<Ts...>{});
}
It could be a "return" value for a metafunction, indicating a failure condition.
template <typename F, typename T>
struct filter_one
: std::conditional_t<F::template apply<T>::value,
typelist<T>,
typelist<>>
{ };
Which is a helper we could use to write a typelist filter metafunction:
template <typename F, typename TL>
struct filter;
template <typename F, typename... Ts>
struct filter<F, typelist<Ts...>>
: concat_t<filter_one<F, Ts>...>
{ };
Both of those are very useful features of typelist<>, and that's just the one class template.

Currying for templates in C++ metaprogramming

This is more of a conceptual question. I'm trying to find the easiest way of converting a two-arg template (the arguments being types) into a one-arg template. I.e., binding one of the types.
This would be the meta-programming equivalent of bind in boost/std. My example includes a possible use-case, which is, passing std::is_same as template argument to a template that takes a one-arg template template argument (std::is_same being a two-arg template), i.e. to TypeList::FindIf. The TypeList is not fully implemented here, neither is FindIf, but you get the idea. It takes a "unary predicate" and returns the type for which that predicate is true, or void if not such type.
I have 2 working variants but the first is not a one-liner and the 2nd uses a rather verbose BindFirst contraption, that would not work for non-type template arguments. Is there a simple way to write such a one-liner? I believe the procedure I'm looking for is called currying.
#include <iostream>
template<template<typename, typename> class Function, typename FirstArg>
struct BindFirst
{
template<typename SecondArg>
using Result = Function<FirstArg, SecondArg>;
};
//template<typename Type> using IsInt = BindFirst<_EqualTypes, int>::Result<Type>;
template<typename Type> using IsInt = std::is_same<int, Type>;
struct TypeList
{
template<template<typename> class Predicate>
struct FindIf
{
// this needs to be implemented, return void for now
typedef void Result;
};
};
int main()
{
static_assert(IsInt<int>::value, "");
static_assert(!IsInt<float>::value, "");
// variant #1: using the predefined parameterized type alias as predicate
typedef TypeList::FindIf<IsInt>::Result Result1;
// variant #2: one-liner, using BindFirst and std::is_same directly
typedef TypeList::FindIf< BindFirst<std::is_same, int>::Result>::Result Result2;
// variant #3: one-liner, using currying?
//typedef TypeList::FindIf<std::is_same<int, _>>::Result Result2;
return 0;
}
Click here for code in online compiler GodBolt.
I think the typical way of doing this is keep everything in the world of types. Don't take template templates - they're messy. Let's write a metafunction named ApplyAnInt that will take a "metafunction class" and apply int to it:
template <typename Func>
struct ApplyAnInt {
using type = typename Func::template apply<int>;
};
Where a simple metafunction class might be just checking if the given type is an int:
struct IsInt {
template <typename T>
using apply = std::is_same<T, int>;
};
static_assert(ApplyAnInt<IsInt>::type::value, "");
Now the goal is to support:
static_assert(ApplyAnInt<std::is_same<_, int>>::type::value, "");
We can do that. We're going to call types that contain _ "lambda expressions", and write a metafunction called lambda which will either forward a metafunction class that isn't a lambda expression, or produce a new metafunction if it is:
template <typename T, typename = void>
struct lambda {
using type = T;
};
template <typename T>
struct lambda<T, std::enable_if_t<is_lambda_expr<T>::value>>
{
struct type {
template <typename U>
using apply = typename apply_lambda<T, U>::type;
};
};
template <typename T>
using lambda_t = typename lambda<T>::type;
So we update our original metafunction:
template <typename Func>
struct ApplyAnInt
{
using type = typename lambda_t<Func>::template apply<int>;
};
Now that leaves two things: we need is_lambda_expr and apply_lambda. Those actually aren't so bad at all. For the former, we'll see if it's an instantiation of a class template in which one of the types is _:
template <typename T>
struct is_lambda_expr : std::false_type { };
template <template <typename...> class C, typename... Ts>
struct is_lambda_expr<C<Ts...>> : contains_type<_, Ts...> { };
And for apply_lambda, we just will substitute the _ with the given type:
template <typename T, typename U>
struct apply_lambda;
template <template <typename...> class C, typename... Ts, typename U>
struct apply_lambda<C<Ts...>, U> {
using type = typename C<std::conditional_t<std::is_same<Ts, _>::value, U, Ts>...>::type;
};
And that's all you need actually. I'll leave extending this out to support arg_<N> as an exercise to the reader.
Yeah, I had this issue to. It took a few iterations to figure out a decent way to do this. Basically, to do this, we need to specify a reasonable representation of what we want and need. I borrowed some aspects from std::bind() in that I want to specify the template that I wish to bind and the parameters that I want to bind to it. Then, within that type, there should be a template that will allow you to pass a set of types.
So our interface will look like this:
template <template <typename...> class OP, typename...Ts>
struct tbind;
Now our implementation will have those parameters plus a container of types that will be applied at the end:
template <template <typename...> class OP, typename PARAMS, typename...Ts>
struct tbind_impl;
Our base case will give us a template type, which I'll call ttype, that'll return a template of the contained types:
template <template <typename...> class OP, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>>
{
template<typename...Us>
using ttype = OP<Ss...>;
};
Then we have the case of moving the next type into the container and having ttype refer to the ttype in the slightly simpler base case:
template <template <typename...> class OP, typename T, typename...Ts, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>, T, Ts...>
{
template<typename...Us>
using ttype = typename tbind_impl<
OP
, std::tuple<Ss..., T>
, Ts...
>::template ttype<Us...>;
};
And finally, we need a remap of the templates that will be passed to ttype:
template <template <typename...> class OP, size_t I, typename...Ts, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>, std::integral_constant<size_t, I>, Ts...>
{
template<typename...Us>
using ttype = typename tbind_impl<
OP
, typename std::tuple<
Ss...
, typename std::tuple_element<
I
, typename std::tuple<Us...>
>::type
>
, Ts...
>::template ttype<Us...>;
Now, since programmers are lazy, and don't want to type std::integral_constant<size_t, N> for each parameter to remap, we specify some aliases:
using t0 = std::integral_constant<size_t, 0>;
using t1 = std::integral_constant<size_t, 1>;
using t2 = std::integral_constant<size_t, 2>;
...
Oh, almost forgot the implementation of our interface:
template <template <typename...> class OP, typename...Ts>
struct tbind : detail::tbind_impl<OP, std::tuple<>, Ts...>
{};
Note that tbind_impl was placed in a detail namespace.
And voila, tbind!
Unfortunately, there is a defect prior to c++17. If you pass tbind<parms>::ttype to a template that expects a template with a particular number of parameters, you will get an error as the number of parameters don't match (specific number doesn't match any number). This complicates things slightly requiring an additional level of indirection. :(
template <template <typename...> class OP, size_t N>
struct any_to_specific;
template <template <typename...> class OP>
struct any_to_specific<OP, 1>
{
template <typename T0>
using ttype = OP<T0>;
};
template <template <typename...> class OP>
struct any_to_specific<OP, 2>
{
template <typename T0, typename T1>
using ttype = OP<T0, T1>;
};
...
Using that to wrap tbind will force the compiler to recognize the template having the specified number of parameters.
Example usage:
static_assert(!tbind<std::is_same, float, t0>::ttype<int>::value, "failed");
static_assert( tbind<std::is_same, int , t0>::ttype<int>::value, "failed");
static_assert(!any_to_specific<
tbind<std::is_same, float, t0>::ttype
, 1
>::ttype<int>::value, "failed");
static_assert( any_to_specific<
tbind<std::is_same, int , t0>::ttype
, 1
>::ttype<int>::value, "failed");
All of which succeed.