Why is a template<typename…> not recognized as instantiatable through template<template<typename> typename>?
我试图任意"绑定"模板参数,但遇到了一个优雅的问题。
为了直接切入底层问题,GCC6.2存在以下问题,但从逻辑上看,它没有问题……
1 2 3 4 5 6 7 8 9 | 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; }; |
…
1 2 3 4 5 6 7 | 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 }; |
您可以在https://godbolt.or g/g/lt9umg中看到这个概念的实际应用(或者更确切地说是错误)。
回到开始的时候,首先我尝试了以下…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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...>; |
稍后,我可能会尝试通过
1 | cout << has_type<long, int, bool, long, float>::value << endl; |
然而,正如我指出的,GCC6.2.0抱怨的是,因为它似乎不认识到,一旦解决了问题,模板实例化实际上是等效的。
只需知道模板参数的数量,并专门为精确的数字解决问题。如果我专攻
1 2 3 4 5 | template<template<typename, typename> typename Tmpl, typename T> struct bind_template<Tmpl, T> { template<typename U> using type = Tmpl<T, U>; }; |
…我们突然编译和评估编译时间没有问题,因为gcc认为
显然,将这个概念抽象出来以允许使用任何模板参数,例如整型常量而不仅仅是类型,这是一个基本问题,与编译器无关。不过,我的问题是多方面的:
在功能上,真正的问题(特别是如果这是C++ 11中一个不可避免的问题)是…
是否有一些优雅的方法可以抽象地绑定模板而不必专门处理每种情况(这里最简单的方法是n种类型)?只要能得到问题1或3的答案就好了。问题3是最重要的,因为在一天结束的时候,工作是最重要的。
显然,我可以专攻(如上图所示)。不过,一个大问题是,以下内容似乎都不起作用(至少根据这个在线编译器的说法)。
1 2 3 4 5 6 7 8 9 10 11 | 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> { }; }; |
这意味着,我不仅必须生成一系列参数,而且还必须通过完整的
进一步扩展这个概念(但仍然保持类型),我计划下一步实现"占位符",就像
这是在C++ 17中固定的。具体而言,根据14.3.3"模板参数",第3段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class ... Types> class C { /* ... */ };
template 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 xb; // OK
X<C> xc; // OK
Y<A> ya; // OK
Y yb; // OK
Y<C> yc; // OK
Z<D> zd; // OK
这里的相关示例是
C++ 14指定上面的示例是不正确的:
1
2
3
4
5
6
7
8 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 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
这一变化来自于2016年底的一篇论文《Dr:Matching of template template arguments excludes compatible templates》。该更改从11月起应用于此提交。委员会自1999年或之前就知道这个问题。
在C++ 17之前,由于JBApple的回答中提到的一个不幸的语言缺陷,该代码的形式不正确。
一个C++ 11的一致解决方案是从无处不在的元函数转换到使用元函数类。一个来自boost.mpl定义的元函数类将是一个具有名为
1 2 | template <class MFC, class... Ts> using apply_t = typename MFC::template apply<Ts...>; |
我们可以通过以下方式将模板提升到元函数类中:
1 2 3 4 5 | template <template <typename...> class Z> struct quote { template <class... Args> using apply = Z<Args...>; }; |
然后,让我们重写
1 2 3 4 5 | template <class MFC, class... Ts> struct bind_template { template <class... Us> using apply = apply_t<MFC, Ts..., Us...>; }; |
然后重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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...>; |
现在,您的初始EDOCX1 5是EDCOX1,6,甚至在C++ 11中。