【发布时间】:2018-10-09 15:57:13
【问题描述】:
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 必须是两个不同的模板。
谁是对的?
【问题讨论】:
-
这不是这种情况的正确参考,而是en.cppreference.com/w/cpp/language/type_alias中使用的措辞“别名模板是一个模板,当专门化时,它等效于替换别名模板的模板参数的结果对于 type-id 中的模板参数”意味着
storage_tpl是独立的模板,其行为与std::void_t -
有趣。如果您想稍微简化一点,您可以从
storage和F中删除typename T参数(显然也删除type_t)和F。 g++ 和 clang++ 的分歧依然存在。 -
在[temp.class.spec.match]/2中说如果部分特化的模板参数可以是来自实际模板参数列表的deduced,其中 deduced 是指向 [temp.deduct] 的链接,其中...从模板参数中扣除模板参数没有任何意义(这是有道理的,因为 [temp.deduct] 部分在 function template specialization 之下.我认为该标准未充分说明。此外,在 Template 下的标准中有一个称为 Type Equivalence 的部分,但没有针对 Template Equivalence的规范>.
-
别名模板结合模板模板参数是非推导上下文,基本上你最终会遇到与此线程stackoverflow.com/questions/52462410/…中相同的问题(部分排序和非推导上下文)。
标签: c++ gcc language-lawyer clang++ template-specialization