C++ GCC/CLang不同意模板参数的部分专门化
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似
#包括
模板
结构存储{
使用类型_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似乎是正确的,因为它知道嵌套模板只是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这个线程中的问题(偏序和非推断上下文)。