Expanding a parameter pack of template<class>classes - c++

Suppose I have some template classes defined as follows
template<template<class>class...>
struct my_class;
template<class>
struct define_template{
template<class>
class type;
};
I need to define an alias template that substitutes define_template::type into my_class
so for three classes I could do this
template<class A, class B, class C>
using my_alias = my_class< define_template<A>::template type,
define_template<B>::template type,
define_template<C>::template type>;
I can't work out the syntax to do this for a variadic template ideally something like this
template<class... T>
using new_class = my_class<define_template<T>::template type...>;
which gives me an error "parameter packs not expanded with '...'
Does anybody know the correct synax?
From the comments below it compiles in clang, I'm using gcc 4.8.2 through Cygwin.

I'm assuming it's a bug in gcc so I've done a workaround for now
// partially specializes a template
template<template<class, class>class TT, class T>
struct TemplateBind{
template<class S>
using type = TT<T, S>;
};
// the workaround
template<template<template<class>class...>class A,
template<class, class>class B,
template<class>class... C>
class workaround {
template<class D, class... E>
static auto get_type(D, E...)
-> typename workaround<A, B, C..., TemplateBind<B, D>::template type>::template type<E...>;
static auto get_type()
->A<C...>;
public:
template<class... T>
using type = decltype(get_type(std::declval<T>()...));
};
// convinience alias
template<template<template<class>class...>class OriginalTemplate,
template<class, class>class Substitution,
class... Types>
using InstatiateVariadicTemplateTemplate = typename workaround<OriginalTemplate, Substitution>::template type<Types...>;
Then we can define a wrapper for define_template
// wrapper alias gets define_template in the right form
template<class A, class B>
using my_wrapper = typename define_template<A>::template type<B>;
and instantiate as follows
template<class... T>
using my_alias = InstatiateVariadicTemplateTemplate<my_class, my_wrapper, T...>;

Related

Is it possible to infer second template argument type for this template class?

I have a template class A
template<template<class> class E, class N>
class A
{}
and I also have a template function that takes A as an argument:
template<class T>
auto make_something(const T& t){}
used like make_something(a) on an object a of A type.
I would like to be able to tell what is the type N from inside the make_something function. Is there any way of achieving this without changing the template argument of make_something?
(In the following I've changed template<class> class E to template<class, class> class E because I used std::vector, which has two type template parameters, for E.)
I have no idea what you're doing, but to ask question about types, you need to write some traits.
I guess something like the following could be a starting point:
// to ask if something is an A at all
template<typename T>
struct IsAnA : std::false_type {};
template<template<class, class> class E, class N>
struct IsAnA<A<E, N>> : std::true_type {};
template<typename T>
constexpr bool is_an_A_v = IsAnA<T>::value;
// to query A's second template arg
template<typename T>
struct SecondTemplArgOfA {};
template<template<class, class> class E, class N>
struct SecondTemplArgOfA<A<E, N>> {
using type = N;
};
template<typename T>
using second_templ_arg_of_A_t = typename SecondTemplArgOfA<T>::type;
// make use of it
template<class T>
auto make_something(const T& t){
if constexpr (is_an_A_v<T>) {
using its2ndTemplArg = second_templ_arg_of_A_t<T>;
// so what?
} else {
// you've fed make_something with a non-A, so
// you can't get its second template arg
}
}
Demo.

Partial template deduction in template argument list

I have the following structs:
template<typename T1, typename T2>
struct A {};
template<typename T>
struct B {};
template<typename A, typename B>
struct C {};
and want to use them as follows:
C<B<int>, A<double, B<int>>> c;
Is there any way to deduce the second template parameter for A, such that I can use it like this?
C<B<int>, A<double>> c;
This should work for any template arguments of C and not just for a particular one (therefore default arguments don't seem to work).
Furthermore a solution for variadic templates would be even better, so instead of
C<B<int>, A<double, B<int>>, A<float, A<double, B<int>>>> c;
something like this would be nice:
C<B<int>, A<double>, A<float>> c;
With a bit of template metaprogramming fun, I was able to solve your problem both for the two-parameter and the variadic case (although in a somewhat narrow way):
// TypeList stuff
template<class... Args>
struct List {
template<class Arg>
using Add = List<Args..., Arg>;
};
template<class List> struct TailI;
template<class A, class... Ar> struct TailI<List<A, Ar...>> {
using type = typename TailI<List<Ar...>>::type;
};
template<class A> struct TailI<List<A>> {
using type = A;
};
template<class List>
using Tail = typename TailI<List>::type;
template<template<class...> class OP, class List> struct rename_impl;
template<template<class...> class OP, class... ListArgs>
struct rename_impl<OP, List<ListArgs...>> {
using type = OP<ListArgs...>;
};
template<template<class...> class OP, class List>
using rename = typename rename_impl<OP, List>::type;
// Actual code solving problem at hand
template<class T1, class T2> struct A{};
template<class... Args> struct C{};
template<class Built, class Next, class... Rest>
struct builder {
using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
using type = typename builder<NewBuilt, Rest...>::type;
};
template<class Built, class Next>
struct builder<Built, Next> {
using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
using type = rename<C, NewBuilt>;
};
template<class First, class... Rest>
using c_builder = typename builder<List<First>, Rest...>::type;
using t = c_builder<int, double, float>;
// Test driver
#include <utility>
static_assert(std::is_same_v<t, C<int, A<double, int>, A<float, A<double, int>>>>, "");
For the simpler case of
template<typename A, typename B> struct C {};
you can use a helper class to provide you the type you need.
template<typename T1, typename T2>
struct C_Helper
{
using type = C<B<T1>, A<T2, B<T1>>>;
};
and use
using C_Type = typename C_Helper<int, double>::type;
C_Type c;
I haven't thought through how that can be extended to support a variadic class template C,

C++ Concepts: checking for template instantiation

Assuming I have a templated type, e.g.
template<typename A, typename B, typename C>
struct mytype { };
How do I write a concept that checks whether a type is an instantiation of that template?
template<typename T>
concept MyType = requires(T x) { ??? }
I can't figure an obvious way of doing it without resolving to old-style specialised detector types or maybe a marker base type.
Using C++17 class template argument deduction, you should be able to do something like this:
template<typename A, typename B, typename C>
struct mytype { };
template<class T>
concept C1 = requires(T x) {
{ mytype{x} } -> std::same_as<T>;
};
mytype{x} uses class template argument deduction to deduce A, B and C, so this is valid if you can construct a mytype<A, B, C> from a T. In particular, this is valid if mytype is copy-constructible since you have an implicitly declared copy-deduction guide similar to:
template <typename A, typename B, typename C>
mytype(mytype<A, B, C>) -> mytype<A, B, C>;
Checking that T is also the constructed mytype instantiation avoid matching other deduction guides, e.g., this would match for any type without the -> std::same_as<T>:
template <class A, class B, class C>
struct mytype {
mytype(A);
};
template <class A>
mytype(A) -> mytype<A, A, A>;
The proposed solution does not work for non copy-constructible classes, even though should be possible to make it work for move-only classes.
Tested with clang and gcc: https://godbolt.org/z/ojdcrYqKv
You can define your own meta-function (type trait) for that purpose:
template <typename T>
struct is_mytype : std::false_type { };
template <typename A, typename B, typename C>
struct is_mytype<mytype<A, B, C>> : std::true_type { };
template <typename T>
concept MyType = is_mytype<T>::value;
But to say the truth, I don't know whether there isn't a way how to defining such a concept directly without the need of a separate metafunction.
You can write a generalized trait to check for specializations:
template <typename T, template <typename...> class Z>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> class Z>
struct is_specialization_of<Z<Args...>, Z> : std::true_type {};
template <typename T, template <typename...> class Z>
inline constexpr bool is_specialization_of_v = is_specialization_of<T,Z>::value;
Which you can make into either a generalized concept:
template<typename T, template <typename...> class Z>
concept Specializes = is_specialization_of_v<T, Z>;
template<typename T>
concept MyType = Specializes<T, mytype>;
or just a specialized one:
template<typename T>
concept MyType = is_specialization_of_v<T, mytype>;
In the interests of terseness:
template<typename T>
concept MyType = requires(T** x) {
[]<typename A, typename B, typename C>(mytype<A, B, C>**){}(x);
};
The double-pointer is necessary to avoid derived-to-base conversions, ex. struct S : mytype<int, int, int> {}.
This doesn't work in clang at present, since it doesn't allow lambdas in unevaluated context. You can workaround by providing a helper variable template:
template<class T> constexpr auto L = []<typename A, typename B, typename C>(mytype<A, B, C>**){};
template<typename T>
concept MyType = requires(T** x) { L<T>(x); };
As long as mytype's template arguments are all types, you can make this even terser using placeholders:
template<typename T>
concept MyType = requires(T** x) { [](mytype<auto, auto, auto>**){}(x); };
At present, this only works in gcc.
If you give your template class some traits you can do the following:
template<typename A, typename B, typename C>
struct mytype {
using a_type = A;
using b_type = B;
using c_type = C;
};
With the associate concept:
template <typename T>
concept is_mytype =
std::is_same_v<
std::remove_const_t<T>,
mytype<typename T::a_type, typename T::b_type, typename T::c_type>>;
Or, if mytype has members of those types you can skip the traits:
template<typename A, typename B, typename C>
struct mytype {
A a_inst;
B b_inst;
C c_inst;
};
Giving the concept:
template <typename T>
concept is_mytype =
std::is_same_v<
std::remove_const_t<T>,
mytype<decltype(T::a_inst), decltype(T::b_inst), decltype(T::c_inst)>>;

Using decltype with macro for explicit instantiation of class member functions

I am attempting to extend upon Can I use decltype (or something similar) for explicit template instantiation without signature duplication? for use with templated member functions, but I am having no luck. Current attempts at making this work look like:
// header
struct my_class
{
template <typename T>
some_type my_func(T val);
};
//source
template <typename T>
some_type my_class::my_func(T val)
{
....
}
// attempt a
template decltype(my_class::my_func<int>) my_class::my_func<int>;
// attempt b
template std::remove_pointer<decltype(&my_class::my_func<int>)>::type my_class::my_func<int>;
Is this possible? If so, any thoughts on how I can make this work?
EDIT:
so it appears that this requires some compiler update only available in clang or gcc 6.1+. The following code provided by #yakk will work for const methods:
template <class T>
struct pointer_to_member_function{};
template <typename pmf_t>
using pointer_to_member_signature
= typename pointer_to_member_function<pmf_t>::signature;
template <class ret_t, class class_t, class...args>
struct pointer_to_member_function<ret_t(class_t::*)(args...) const>
{
using signature = ret_t(args...) const;
};
However, the following modification does compile across all const-ness:
template <class function_t, class class_t>
struct pointer_to_member_function<function_t class_t::*>
{
using signature = function_t;
};
I do not know if this is standard compliant, but it works in clang:
template<class Pmf>
struct pmf_sig{};
template<class Pmf>
using pmf_sig_t=typename pmf_sig<Pmf>::type;
template<class R, class T, class...Args>
struct pmf_sig<R(T::*)(Args...)>{
using type=R(Args...);
};
then const and const& and && support:
template<class R, class T, class...Args>
struct pmf_sig<R(T::*)(Args...) const>{
using type=R(Args...) const;
};
template<class R, class T, class...Args>
struct pmf_sig<R(T::*)(Args...) const&>{
using type=R(Args...) const&;
};
template<class R, class T, class...Args>
struct pmf_sig<R(T::*)(Args...) const&&>{
using type=R(Args...) const&&;
};
template<class R, class T, class...Args>
struct pmf_sig<R(T::*)(Args...) &&>{
using type=R(Args...) &&;
};
template<class R, class T, class...Args>
struct pmf_sig<R(T::*)(Args...) &>{
using type=R(Args...) &;
};
The use:
template<> pmf_sig_t<decltype(&my_class::my_func<int>)> my_class::my_func<int>;
Or:
template pmf_sig_t<decltype(&my_class::my_func<int>)> my_class::my_func<int>;
The idea is that my_class:: is not part of the signature in some sense.
live example.

Template template and partial specialization: a puzzle

Consider the following code:
template<typename>
struct S { };
template<typename, typename>
struct B;
template <typename R, typename... Args, template<class> class C>
struct B<R(Args...), C<R>> {
void f() { }
};
int main() {
B<void(), S<void>> b;
b.f();
}
It compiles and has no problem.
Anyway, whenever one decides to use B, it has to provide two types.
What I'd like to achieve is to default somehow the second parameter (I know, partial specializations do not accept a default for their parameters) and let an user define it's type as B<void()> instead of B<void(), S<void>>.
Unfortunately, because of template template, partial specialization and the dependency existent between the parameters, all together they lead to a puzzle against which I'm struggling since a couple of hours.
Is there any clever solution to do that?
So far, I have been able to solve it with intermediate structures, but I don't like it so much...
Partial specializations don't accept default parameters, but the primary does. You can just add it there:
template<typename Sig, typename X = S<return_type_t<Sig>>>
struct B;
Then all you need to do is implement a return type metafunction for a signature. Something like:
template <class Sig>
struct return_type;
template <class Sig>
using return_type_t = typename return_type<Sig>::type;
template <class R, class... Args>
struct return_type<R(Args...)> {
using type = R;
};
You may create an helper class for that:
template <typename T> struct default_arg;
template <typename R, typename... Args>
struct default_arg<R(Args...)>
{
using type = S<R>;
};
template<typename Sign, typename T = typename default_arg<Sign>::type>
struct B;
Demo
Here we change B into a template alias.
B_t does the default arg work.
B_impl is the implementation of B without any default args.
B is a using alias that gets the result of B_t.
template<class> struct S {};
template<class, class>
struct B_impl;
template<class R, class... Args, template<class...> class C>
struct B_impl<R(Args...), C<R>> {
void f() { }
};
template<class, class=void>
struct B_t;
template<class R, class...Args>
struct B_t<R(Args...), void>:
B_t<R(Args...),S<R>>
{};
template<class R, class... Args, template<class...> class C>
struct B_t<R(Args...), C<R>> {
using type=B_impl<R(Args...), C<R>>;
};
template<class Sig, class Z=void>
using B=typename B_t<Sig,Z>::type;
The downside is that pattern-matching on B won't work well.