GCC/CLang对模板模板参数的部分特化不一致

dvd*_*dvd 10 c++ gcc template-specialization language-lawyer clang++

GCC和clang对此代码持不同意见.

#include <type_traits>

template <typename T, template <typename...> typename Tpl> 
struct storage {
    using type_t = T;

    template <typename... Args>
    using storage_tpl = Tpl<Args...>;
};

template <typename T, template <typename...> typename>
struct F{
    constexpr static int x = 1;
};

template <typename T >
struct F<T, std::void_t>{
    constexpr static int x = 2;
};

int f() {
    using S = storage<int, std::void_t>;

    static_assert(F<int, S::storage_tpl>().x == 2);

    return F<int, S::storage_tpl>().x;
}
Run Code Online (Sandbox Code Playgroud)

根据铿锵S::storage_tpl不是std::void_t; 结果,它选择主模板F而不是部分特化,从而选择断言.

乍一看,它看起来像GCC是正确的,因为它知道该嵌套模板仅仅是一个别名std::void_t,但也许是太聪明,该标准要求S::storage_tplstd::void_t必须是两个不同的模板.

谁是对的?

Sha*_*our 4

目前看来这一点尚未明确,并且向 TC 透露,CWG 缺陷报告 1286似乎涵盖了这一点,其中指出:

\n\n
\n

通过更改 17.5 [temp.type]\n 第 1 段中的示例,解决了问题 1244

\n\n
 template<template<class> class TT> struct X { };\n  template<class> struct Y { };\n  template<class T> using Z = Y<T>;\n  X<Y> y;\n  X<Z> z;\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n
  template<class T> struct X { };\n  template<class> struct Y { };\n  template<class T> using Z = Y<T>;\n  X<Y<int> > y;\n  X<Z<int> > z;\n
Run Code Online (Sandbox Code Playgroud)\n\n

事实上,最初的意图是这个例子应该是正确的。然而,缺少规范性措辞。17.6.7 [temp.alias] 的当前措辞仅处理别名模板的特化与替换后的类型 ID 的等价性。需要添加措辞来指定在什么情况下别名模板本身等同于类模板。

\n
\n\n

并提出以下决议:

\n\n
\n

添加以下内容作为 17.6.7 [temp.alias]\n 第 2 段之后的新段落:

\n\n

当别名模板(称为 A)声明中的 type-id 包含 simple-template-id 时,其中 template-argument-list\n 包含准确命名 A\n 的每个模板参数的标识符列表一旦按照它们在 A 的模板参数列表中出现的顺序相同,如果 A 和 T 有,别名模板就相当于 simple-template-id 中命名的模板(称为 T) \n 相同数量的模板参数。[脚注:此规则是\n及物性的:如果一个别名模板 A 等价于另一个等价于类模板 C 的别名模板 B,则 A 也等价于 C,并且 A 和 B 也等价对彼此。\xe2\x80\x94end\n 脚注] [示例:

\n\n
  template<typename T, U = T> struct A;\n\n  template<typename V, typename W>\n    using B = A<V, W>;                // equivalent to A\n\n  template<typename V, typename W>\n    using C = A<V>;                   // not equivalent to A:\n                                      // not all parameters used\n\n  template<typename V>\n    using D = A<V>;                   // not equivalent to A:\n                                      // different number of parameters\n\n  template<typename V, typename W>\n    using E = A<W, V>;                // not equivalent to A:\n                                      // template-arguments in wrong order\n\n  template<typename V, typename W = int>\n    using F = A<V, W>;                // equivalent to A:\n                                      // default arguments not considered\n\n  template<typename V, typename W>\n    using G = A<V, W>;                // equivalent to A and B\n\n  template<typename V, typename W>\n    using H = E<V, W>;                // equivalent to E\n\n  template<typename V, typename W>\n    using I = A<V, typename W::type>; // not equivalent to A:\n                                      // argument not identifier\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

\xe2\x80\x94结束示例]

\n
\n
\n\n

但此解决方案存在问题,并且缺陷报告仍然有效。

\n