Why is the template <typename ...> not recognized as an instance through the template <template <typename> typename>?
I try to arbitrarily "bind" the template parameters, but I run into an elegance problem.
To go directly to the main issue, gcc 6.2 has a problem with the following, but logically I don't see a problem with this ...
template<template<typename, typename> P, typename A, typename B> struct foo { static constexpr bool value = P<A, B>::value; }; template<typename...Ts> struct bar { static constexpr bool value = true; }; ... foo specified by bar , such as foo<bar, void, void> , should lead to the creation of bar<void, void> (which is true), who is value member true , and therefore foo<bar, void, void>::value also true . In fact, this should (in my opinion) lead to the creation of instances conceptually similar to ...
struct bar<void, void> { static constexpr bool value = true; }; struct foo<bar, void, void> { static constexpr bool value = bar<void, void>::value; //which is true }; You can see this concept in action (or rather about an error) here https://godbolt.org/g/lT9umg .
Back to the top now, first I tried the following ...
template<typename...> struct type_list { }; template<template<typename...> typename Tmpl, typename...Ts> struct bind_template { template<typename...Us> using type = Tmpl<Ts..., Us...>; }; template<template<typename> typename Predicate, typename...Ts> struct has_matching_type { private: template<template<typename> typename, typename, typename=void> struct helper: std::false_type { }; template<template<typename> typename P, typename U, typename...Us> struct helper<P, type_list<U, Us...>, typename std::enable_if<P<U>::value>::type>: std::true_type { }; template<template<typename> typename P, typename U, typename...Us> struct helper<P, type_list<U, Us...>, typename std::enable_if<!P<U>::value>::type>: helper<P, type_list<Us...>> { }; public: static constexpr bool value = helper<Predicate, type_list<Ts...>>::value; }; template<typename T, typename...Ts> using has_type = has_matching_type<bind_template<std::is_same, T>::template type, Ts...>; Later I can try to create an instance via has_type<T, Ts...> , for example ...
cout << has_type<long, int, bool, long, float>::value << endl; However, since I pointed out that gcc 6.2.0 complains because it does not seem to recognize that once the permission is complete, creating the template will be pragmatically equivalent.
A simple knowledge of the number of template parameters and specialization for this exact number solves the problem. If I specialize bound_template std::is_same<LHS, RHS> keeping in mind ...
template<template<typename, typename> typename Tmpl, typename T> struct bind_template<Tmpl, T> { template<typename U> using type = Tmpl<T, U>; }; ... we suddenly compile and evaluate the compilation without any problems, because gcc sees bind_template<std::is_same, long>::type as one of the type parameters.
Obviously, abstracting this concept to allow any template parameters, such as integral constants, and not just types, is a fundamental problem regardless of the compiler. Just focusing on types for a minute, my question is a few:
- I missed something here conceptually, and does the compiler really do exactly what should be obvious to me?
- If this is not the case, does it violate C ++ 11 standards not directed by the standards, or is it a compiler dependency?
- Is there any elegant way that I can get around this, regardless of the answers to my first two questions?
Functionally real question (especially if this is an inevitable problem in C ++ 11) ...
Is there any elegant way to abstractly bind patterns without having to specialize in each case (the simplest here is n number of types)?
Just being able to get answers to questions 1 or 3 would be great. Question 3 is the most important, because at the end of the day it matters.
Obviously, I can specialize (as shown above). The big problem is that even the following doesn't work (at least according to this online compiler ) ...
template<template<typename...> class Tmpl, typename... Ts> struct bind_helper { template<typename... Us> struct type: Tmpl<Ts..., Us...> { }; template<typename A> struct type<A>: Tmpl<Ts..., A> { }; template<typename A, typename B> struct type<A, B>: Tmpl<Ts..., A, B> { }; template<typename A, typename B, typename C> struct type<A, B, C>: Tmpl<Ts..., A, B, C> { }; }; This means that I will not only have to generate a set of parameters, but I will also have to map external parameters using the full bind_template specialization. This is quickly becoming (in fact) a binomial problem.
Continuing this concept (but still adhering to types), I planned the following to implement "placeholders" in the same way that std::bind uses placeholders (which would work quite elegantly, because I would just clear and join the list in the index). Obviously, this is too much mess to act without a more abstract approach.
This is fixed in C ++ 17. In particular, from 14.3.3 βTemplate template argumentsβ, clause 3:
template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<class ... Types> class C { /* ... */ }; template<auto n> class D { /* ... */ }; template<template<class> class P> class X { /* ... */ }; template<template<class ...> class Q> class Y { /* ... */ }; template<template<int> class R> class Z { /* ... */ }; X<A> xa; // OK X<B> xb; // OK X<C> xc; // OK Y<A> ya; // OK Y<B> yb; // OK Y<C> yc; // OK Z<D> zd; // OK Here is an example: X<C> . This works in g ++ 7 with the -std=c++1z flag.
C ++ 14 indicates that the above example is poorly formed:
template<class T> class A { /β ... β/ }; template<class T, class U = T> class B { /β ... β/ }; template <class ... Types> class C { /β ... β/ }; template<template<class> class P> class X { /β ... β/ }; template<template<class ...> class Q> class Y { /β ... β/ }; X<A> xa; // OK X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored X<C> xc; // ill-formed: a template parameter pack does not match a template parameter The change occurred at the end of 2016 from the DR article : Matching the template-argument template excludes compatible templates . This change has been applied to this commit since November . This problem was known to the committee from 1999 onwards .
Prior to C ++ 17, this code is poorly formed as is due to an unsuccessful language defect, as indicated in jbapple's answer .
A solution compatible with C ++ 11 would be to switch from using metafunds everywhere to using metafound classes everywhere. The metafunction class, from the definition of Boost.MPL, will be a type that has a template alias named apply . What we can call in this way:
template <class MFC, class... Ts> using apply_t = typename MFC::template apply<Ts...>; We can raise the template to the metafunction class via:
template <template <typename...> class Z> struct quote { template <class... Args> using apply = Z<Args...>; }; Then bind_template to take the metafunction class instead of the template:
template <class MFC, class... Ts> struct bind_template { template <class... Us> using apply = apply_t<MFC, Ts..., Us...>; }; And then has_matching_type to take the metafunction class instead of the template:
template<class Predicate, class... Ts> struct has_matching_type { private: template<class> struct helper: std::false_type { }; template<typename U, typename...Us> struct helper<type_list<U, Us...>> : std::conditional< apply_t<Predicate, U>::value, std::true_type, helper<type_list<Us...>> >::type { }; public: static constexpr bool value = helper<type_list<Ts...>>::value; }; template<class T, class... Ts> using has_type = has_matching_type<bind_template<quote<std::is_same>, T>, Ts...>; And now your initial has_type<long, int, bool, long, float>::value is true , even in C ++ 11.