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

时间:2018-02-20 20:46:10

标签: c++ templates clang language-lawyer template-specialization

考虑以下计划:

    'mysql' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '3306'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'unix_socket' => env('DB_SOCKET', ''),

Clang拒绝错误:

  

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

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

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

修改

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

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

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

int main() {}

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

现在我想问:

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

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

2 个答案:

答案 0 :(得分: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已经完成。

然后,要执行此匹配,将应用C ++ 17中引入的规则[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>{});
  • 对于(1)模板参数,继承...P`` is AType`的推导出的参数。

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

  

[...] 在部分订购期间,如果Ai最初是包装扩展:

     
      
  • 如果P不包含与Ai对应的模板参数,则为Ai   被忽略;

  •   
  • 否则,如果Pi不是包扩展,则模板参数扣除失败。

  •   

此处Ai为ATypePackPiP的函数参数中的template<class P> void p_foo_partial(foo<P>)

因此,粗体引用此规则,模板参数推导失败(2),因此模板模板参数&#34;类P&#34; 比其参数更专业。所以调用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的部分特化。

答案 1 :(得分:0)

不幸的是不是这样......

  • 我无论如何都不想尝试进入标准决定不允许以这种方式表达部分专业化的“为什么”——(或者如果关于它的具体措辞已经忘记了)
  • 这里有些人无疑比我更擅长解读标准措辞的复杂微妙之处......;)

也就是说……

  • 如果有人正在寻找解决此问题的方法 - (完全标准的方法) - 这里的信息和示例很可能有助于解决您的问题...


° 模板参数数量。

  • 在展示可以实现基于模板参数的参数数量的专业化的方法之前,我将简要谈谈模板参数本身。

(我可能需要更正措辞,但要点应该足够清楚)

模板的全部要点,当涉及到使用它们时:

  • 使用别名。
  • 带或不带参数。
  • 在调度特性内。
  • 在检测特征内。
  • 在专业领域内。

是关于尽可能最好地理解何时必须将其接收的类型称为参数,以及何时将它们称为参数强>...

一开始对您来说似乎很明显,但您可能也会惊讶地看到我发布了一个非常详细的解决方案,回答了另一个问题,以检测声明的参数的数量 通过模板。

该解决方案首先尝试'计数'传递给模板的参数来描述搜索过程。最后,因为解决方案中的所有内容从未完全定义的模板的前向声明上完成,我意识到这是一个检测算法,因为结果是在没有Victim模板被永远实例化的情况下获得的...

  • 完整详细的解释和实现:here


° 模板模板参数专业化。

(回到最初的疑惑)

当我发现这个问题时,我试图做完全相同类型的专业化,如问题的第一个代码示例所示,以创建一些实现选择器之王...< /p>

我对自己说:“好吧,好像不能那样做……”

由于我已经为检测模板声明的参数数量(通过上面的链接引用)所做的,我很快找到了一种产生预期效果的方法。我认为它肯定对其他人有用,因此我决定将其发布在这里...

我认为代码是不言自明的,所以不需要更多的解释。

// Helper to make the static_assert 'name-dependent'.
template<typename...> using AlwaysFalse = std::false_type;

// Primary template should only signal the error...
template<typename Trait>
struct ImplSelect {
    static_assert(
        AlwaysFalse<Trait>::value,
        "Missing specialization..."
    );
};

// Specialization for a template with one type parameter.
template<template <typename> class Trait, typename T>
struct ImplSelect<Trait<T>> { using Type = Trait<int>; };

// Specialization for a template with two type parameters.
template<template <typename, typename> class Trait, typename T, typename U>
struct ImplSelect<Trait<T, U>> { using Type = Trait<int, int>; };

/* [ Note ] These are ONLY forward declarations.
 *          They aren't instatiated within this code.
 */
template<typename T>             struct Victim1;
template<typename T, typename U> struct Victim2;

/* [ Note ] Opaque type tags (forward declarations).
 *          Allows for further tag dispatching if desired.
 */
struct Tag1; struct Tag2;

/* Selection of Victim1<int> and Victim2<int, int>.
 * Not instantiated yet...
 */
using Impl1 = typename ImplSelect<Victim1<Tag1>>::Type;
using Impl2 = typename ImplSelect<Victim2<Tag1, Tag2>>::Type;
享受吧!