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;
}
根据 clangS::storage_tpl
不是std::void_t
;因此,它选择了主模板 F 而不是部分特化,从而断言。
乍一看,GCC 似乎是对的,因为它理解嵌套模板只是std::void_t
的别名,但也许它太聪明了,标准要求S::storage_tpl
和std::void_t
必须是两个不同的模板。
谁是对的?
看起来目前尚未指定,并且 h/t 到 T.C. 看起来CWG 缺陷报告 1286 涵盖了这一点,其中说:
问题 1244 已通过更改 17.5 中的示例得到解决 [temp.type] 第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;
自
template<class T> struct X { }; template<class> struct Y { }; template<class T> using Z = Y<T>; X<Y<int> > y; X<Z<int> > z;
事实上,初衷是这个例子应该是 按书面正确;然而,使之成为现实的规范性措辞是 失踪。17.6.7 [temp.alias] 的当前措辞仅涉及 别名模板的专用化与 替换后的类型 ID。需要添加措辞,指定在 什么情况下别名模板本身等效于类 模板。
并提出以下决议:
在 17.6.7 [临时别名] 之后添加新段落 第2段:
当别名模板声明中的类型 id(称为 A(由一个简单的模板 id 组成时,其中模板参数列表 由命名 A 的每个模板参数的标识符列表组成 正好一次,与它们在 A 中出现的顺序相同 模板参数列表,别名模板等效于 在简单模板 ID 中命名的模板(称为 T(,如果 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
—结束示例]
但此解决方案存在问题,缺陷报告仍处于活动状态。