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;
}
根据叮当语S::storage_tpl
不是std::void_t
;结果,它选择了主模板F而不是部分专业化,从而选择了断言。
乍一看,GCC似乎是正确的,因为它了解嵌套模板只是std::void_t
的别名,但它可能太聪明了,标准要求S::storage_tpl
和{{1 }}必须是两个不同的模板。
谁是对的?
答案 0 :(得分:3)
当前似乎尚未指定,h/t to T.C.似乎已被CWG defect report 1286覆盖,它表示:
通过更改17.5 [temp.type]中的示例来解决问题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;
到
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]措词仅涉及 别名模板的专业化与 替换后的type-id。需要添加措辞,具体说明如下 在什么情况下别名模板本身等效于类 模板。
并提出了以下解决方案:
将以下内容作为新段落添加到17.6.7 [temp.alias]之后 第2段:
当别名模板声明(称为A)中的type-id由一个simple-template-id组成时,其中template-argument-list 由命名A的每个模板参数的标识符列表组成 完全按照它们在A中出现的顺序排列一次 template-parameter-list,别名模板等效于 如果A和T包含在simple-template-id中命名的模板(称为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
-结束示例]
但是此解决方法存在问题,缺陷报告仍处于活动状态。