GCC/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;
}

根据 clangS::storage_tpl不是std::void_t;因此,它选择了主模板 F 而不是部分特化,从而断言。

乍一看,GCC 似乎是对的,因为它理解嵌套模板只是std::void_t的别名,但也许它太聪明了,标准要求S::storage_tplstd::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

—结束示例]

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

最新更新