为什么模板<typename ...>无法通过模板<template <typename> typename>识别为可实例化?

mr.*_*bbe 8 c++ gcc templates variadic-templates c++11

我试图任意"绑定"模板参数,但遇到了一个优雅的问题.

为了直接解决潜在的问题,gcc 6.2有以下问题,但从逻辑上讲,我认为它没有问题......

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;
};
Run Code Online (Sandbox Code Playgroud)

... foo给出barfoo<bar, void, void>应导致的实例化bar<void, void>(这是有效的),谁的value部件true,因此foo<bar, void, void>::value同样是true.实际上,这应该(在我看来)导致实例化的结构在概念上类似于......

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
};
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到这个概念(或者更确切地说是错误)https://godbolt.org/g/lT9umg.

回到现在开始,首先我尝试了以下......

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...>;
Run Code Online (Sandbox Code Playgroud)

后来我可能会尝试通过has_type<T, Ts...>诸如... 进行实例化

cout << has_type<long, int, bool, long, float>::value << endl;
Run Code Online (Sandbox Code Playgroud)

但是,正如我指出gcc 6.2.0抱怨因为它似乎没有认识到,一旦完成解析,模板实例化在实用上是等效的.

只需知道模板参数的数量并专门针对该确切数字就可以解决问题.如果我专门bound_template饲养std::is_same<LHS, RHS>记...

template<template<typename, typename> typename Tmpl, typename T>
struct bind_template<Tmpl, T> {
    template<typename U>
    using type = Tmpl<T, U>;
};
Run Code Online (Sandbox Code Playgroud)

...我们突然编译和评估编译时没有问题,因为gcc认为bind_template<std::is_same, long>::type完全采用一个类型参数.

显然,将这​​个概念抽象出来以允许任何模板参数,例如积分常数而不仅仅是类型,无论编译器如何都是一个基本问题.仅仅关注一分钟的类型,我的问题是多方面的:

  1. 我在概念上遗漏了什么,编译器实际上正在做的事情对我来说应该是显而易见的吗?
  2. 如果没有,这是否违反了C++ 11标准,不是由标准指导,还是编译器依赖?
  3. 无论我前两个问题的答案是什么,我是否有一些优雅的方式可以解决这个问题?

从功能上讲,真正的问题(特别是如果这是C++ 11中不可避免的问题)是......

是否有一些优雅的方式我可以抽象地绑定模板而不必专门针对每个案例(这里最简单的是n种类型)?

只要能够得到问题1或3的答案就会很棒.问题3是最重要的,因为在一天结束时,重要的是有效的.

显然,我可以专攻(如上所示).一个很大的问题是,即使以下似乎也不起作用(至少根据这个在线编译器)...

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> { };
};
Run Code Online (Sandbox Code Playgroud)

这意味着,我不仅要生成一堆参数,还要通过完全bind_template专业化来匹配外部参数.这很快成为(实际上)二项式问题.

进一步扩展这个概念(但仍然保持类型),我计划下一步实现"占位符",std::bind使用占位符(这将相当优雅,因为我只是剥离并重新加入索引列表).显然,如果没有更抽象的方法,这就太麻烦了.

jba*_*ple 10

这在C++ 17中得到修复.具体而言,从14.3.3"模板模板参数",第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
Run Code Online (Sandbox Code Playgroud)

这里的相关例子是X<C>.这适用于带有标志的g ++ 7 -std=c++1z.

C++ 14指定上面的示例格式错误:

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
Run Code Online (Sandbox Code Playgroud)

这一变化发生在2016年末的DR论文中:匹配模板模板 - 参数排除了兼容的模板.此更改自11月起应用于此提交.该委员会自1999年或之前知道这个问题.