C++ GCC/CLang不同意模板参数的部分专门化

C++ GCC/CLang不同意模板参数的部分专门化,c++,gcc,language-lawyer,clang++,template-specialization,C++,Gcc,Language Lawyer,Clang++,Template Specialization,GCC和clang对此代码有不同意见 #包括 模板 结构存储{ 使用类型_t=t; 模板 使用存储\u tpl=tpl; }; 模板 结构F{ constexpr static int x=1; }; 模板 结构F{ constexpr静态int x=2; }; int f(){ 使用S=存储; 静态断言(F().x==2); 返回F().x; } 根据clangS::storage\u tpl不是std::void\u t;因此,它选择主模板F而不是部分专门化,从而选择断言 乍一看,GCC似

GCC和clang对此代码有不同意见

#包括
模板
结构存储{
使用类型_t=t;
模板
使用存储\u tpl=tpl;
};
模板
结构F{
constexpr static int x=1;
};
模板
结构F{
constexpr静态int x=2;
};
int f(){
使用S=存储;
静态断言(F().x==2);
返回F().x;
}
根据clang
S::storage\u tpl
不是
std::void\u t
;因此,它选择主模板F而不是部分专门化,从而选择断言

乍一看,GCC似乎是正确的,因为它知道嵌套模板只是
std::void_t
的别名,但它可能太聪明了,标准要求
S::storage_tpl
std::void_t
必须是两个不同的模板


谁是对的?

目前似乎还没有明确说明这一点,而且这一点似乎包含在以下内容中:

通过更改17.5[临时类型]中的示例,解决了1244问题 第1段

 template<template<class> class TT> struct X { };
  template<class> struct Y { };
  template<class T> using Z = Y<T>;
  X<Y> y;
  X<Z> z;
模板结构X{};
模板结构Y{};
使用Z=Y的模板;
xy;
xz;

  template<class T> struct X { };
  template<class> struct Y { };
  template<class T> using Z = Y<T>;
  X<Y<int> > y;
  X<Z<int> > z;
模板结构X{};
模板结构Y{};
使用Z=Y的模板;
xy;
xz;
事实上,最初的意图是这个例子应该是 笔迹正确;然而,这样做的规范性措辞是错误的 丢失的17.6.7[临时别名]的当前措辞仅涉及 别名模板的专门化与 替换后的类型id。需要添加措辞,说明以下内容: 别名模板本身在什么情况下等同于类 模板

并提出以下决议:

在17.6.7[临时别名]之后添加以下内容作为新段落 第2段:

当别名模板声明中的类型id(称为A)包含一个简单的模板id时,模板参数列表 由一个标识符列表组成,这些标识符命名 按照它们在A中出现的相同顺序恰好出现一次 模板参数列表,别名模板等效于 如果A和T具有 相同数量的模板参数。[脚注:这条规则是 可传递性:如果一个别名模板A等效于另一个别名 模板B相当于类模板C,那么a也是 等效于C,A和B也相互等效-结束 脚注][示例:

  template<typename T, U = T> struct A;

  template<typename V, typename W>
    using B = A<V, W>;                // equivalent to A

  template<typename V, typename W>
    using C = A<V>;                   // not equivalent to A:
                                      // not all parameters used

  template<typename V>
    using D = A<V>;                   // not equivalent to A:
                                      // different number of parameters

  template<typename V, typename W>
    using E = A<W, V>;                // not equivalent to A:
                                      // template-arguments in wrong order

  template<typename V, typename W = int>
    using F = A<V, W>;                // equivalent to A:
                                      // default arguments not considered

  template<typename V, typename W>
    using G = A<V, W>;                // equivalent to A and B

  template<typename V, typename W>
    using H = E<V, W>;                // equivalent to E

  template<typename V, typename W>
    using I = A<V, typename W::type>; // not equivalent to A:
                                      // argument not identifier
模板结构A;
模板
使用B=A;//相当于A
模板
使用C=A;//不等同于A:
//并非所有参数都已使用
模板
使用D=A;//不等同于A:
//不同数量的参数
模板
使用E=A;//不等同于A:
//模板参数顺序错误
模板
使用F=A;//相当于A:
//未考虑默认参数
模板
使用G=A;//相当于A和B
模板
使用H=E;//相当于E
模板
使用I=A;//不等同于A:
//参数不是标识符
-[结束示例]


但此解决方案存在问题,缺陷报告仍处于活动状态。

这不是此类情况的适当参考,但“别名模板是一个模板,在专用化时,相当于用别名模板的模板参数替换类型id中的模板参数的结果”这意味着
storage\u tpl
是独立的模板,其行为与
std::void\t
相同。如果您想简化一点,可以从
存储
中删除
typename T
参数(显然,也可以从
type\u T
中删除)和
F
中删除。g++和clang++之间的分歧仍然存在。在[temp.class.spec.match]/2中,说明了部分专门化的模板参数是否可以来自实际模板参数列表,其中Decreated是指向[temp.Decrete]的链接,其中。。。从模板参数中扣除模板参数没有任何意义(因为[temp.Decrete]部分在函数模板专用化下。我认为该标准未指定。更多信息,标准中有一部分称为模板下的类型等效,但没有模板等效的规范。别名模板与模板模板参数组合在一起是非推断上下文,基本上你会得到相同的pr这个线程中的问题(偏序和非推断上下文)。