首页 文章

可变参数模板模板参数可以是部分专用的吗?

提问于
浏览
17

考虑以下程序:

template<template<typename ...> class>
struct foo {};

template<template<typename> class C>
struct foo<C> {};

int main() {}

Clang拒绝了它的错误:

类模板部分特化不会专门化任何模板参数

即使在最新的clang 7.0 HEAD中,请参阅demo here . 但是,gcc accepts it .

请参阅[temp.class.spec],其中说明了部分特化规则,我找不到任何禁止该模板部分特化的内容 . 特别是,专业化确实是more specialized,错误消息看起来不正确 .

EDIT:

但是,gcc的行为也是异常的,请考虑以下程序:

#include <iostream>

template<template<typename ...> class>
struct foo { void show() { std::cout << "Primary.\n"; } };

template<template<typename> class C>
struct foo<C> { void show() { std::cout << "Specialized.\n"; } };

template<class...> struct bar {};

int main() {
    foo<bar> f;
    f.show();
}

事实证明,gcc在这种情况下使用专用版本,请参阅here .

现在我想问:

  • 是标准允许的这种部分专业化吗?

  • 哪个编译器正确? (一个/全部/没有?)

1 回答

  • 2

    这最终似乎是新C模板模板参数推导partial support for this feature的实现中的GCC错误 .

    要确定部分特化是否比类模板更专业化,partial ordering is applied to 2 corresponding synthetized functions

    //template class:
    template<template<class...>class P> void f_foo0(foo<P>);
    
    //Partial specialization
    template<template<class P> class P> void f_foo_partial0(foo<P>);
    

    然后尝试通过使用与其他函数参数对应的参数调用每个函数来执行模板参数推导(参见[temp.func.order]

    template<class P> struct Atype{};
    template<class ...P> struct ATypePack{};
    
    //Is f_foo_partial at least as specialized as f_foo?
    f_foo(foo<AType>{});
    
    //Is f_foo at least as specialized as f_foo_partial?
    f_foo_partial(foo<ATypePack>{});
    

    如果模板参数成功(1),那么 f_foo_partial 至少与 f_foo 一样专业 . 如果模板参数成功(2),则 f_foo 至少与 f_foo_partial 一样专门化 . (参见[temp.deduct.partial]) . 然后,如果只有一个至少与另一个一样专业,那么它就是更专业的那个 .

    所以to check if template argument is deductible,然后deduction of template argument from a type正在执行 .

    然后,要执行此匹配,应用C17中引入的规则[temp.arg.template]/3

    当P至少与模板参数A一样专用时,模板参数匹配模板模板参数P. [...]

    并且[temp.arg.template] / 4指定使用这些发明的两个函数将与前面的情况类似地执行此排序:

    template<class...> struct X{};
    //for the argument
    template<class...P> void f_targ(X<P...>);
    
    //Partial specialization
    template<class P> void f_tparam(X<P>);
    
    struct Atype{};
    struct ATypePack{};
    
    //Is template template parameter at least as specialized template template arg?
    f_targ(X<AType>{});
    
    //Is template template arg at least as specialized as template template parameter?
    f_tparam(X<ATypePack>{});
    
    • for(1)模板参数成功推导出 ...P`` is AType`的参数 .

    • for(2)有一个特殊规则,仅适用于模板部分排序[temp.deduct.type] /9.2的情况:

    [...]在部分排序期间,如果Ai最初是包扩展:如果P不包含与Ai对应的模板参数,则忽略Ai;否则,如果Pi不是包扩展,则模板参数推断失败 .

    这里的Ai是 ATypePack ,而 Pitemplate<class P> void p_foo_partial(foo<P>) 的函数参数中的 P .

    因此,由于以粗体引用的此规则,模板参数推导在(2)中失败,因此模板模板参数"class P"比其参数更专业 . 所以电话 f_foo_partial(foo<ATypePack>{}) 已经形成 .

    另一方面,相同的规则,由于[temp.arg.temp] / 3,对 f_foo(AType{}) 的调用很好 .

    [...]如果P包含参数包,那么如果每个A的模板参数与P的模板参数列表中的相应模板参数匹配,则A也匹配P. [...]

    所以 f_foo_partial 并不比 f_foo 更专业,所以 template<class<class > class C> struct foo<C>; 不是模板 foo 的部分特化 .

相关问题