下面的代码应该按照C++标准编译吗?

Pre*_*nik 17 c++ gcc templates sfinae language-lawyer

#include <type_traits>

template <typename T>
struct C;

template<typename T1, typename T2>
using first = T1;

template <typename T>
struct C<first<T, std::enable_if_t<std::is_same<T, int>::value>>>
{
};

int main ()
{
}
Run Code Online (Sandbox Code Playgroud)

不同编译器编译的结果:

MSVC:

错误C2753:'C':部分特化与主模板的参数列表不匹配

GCC-4.9:

错误:部分特化'C'不专门化任何模板参数

clang所有版本:

错误:类模板部分特化没有专门化任何模板参数; 要定义主模板,请删除模板参数列表

gcc-5 +:成功编译

另外我想指出那些琐碎的专业化:

template<typename T>
struct C<T>
{
};
Run Code Online (Sandbox Code Playgroud)

成功地无法由gcc编译.因此,似乎它认为我原始示例中的专业化是非平凡的.所以我的问题是 - 这样的模式是否被C++标准明确禁止?

Col*_*mbo 5

关键段落是[temp.class.spec] /(8.2),它要求部分专业化比主要模板更专业。什么锵却埋怨的是参数列表是等同于主模板的:这已移出[temp.class.spec] /(8.3)通过发行2033(其中指出,需求是多余的)最近,所以不是招”尚未在Clang中实现。但是,鉴于它已接受您的代码段,因此它显然已在GCC中实现;它甚至可以编译以下代码,也许是出于与编译代码相同的原因(它也只能从版本5开始运行):

template <typename T>
void f( C<T> ) {}

template <typename T>
void f( C<first<T, std::enable_if_t<std::is_same<T, int>::value>>> ) {}
Run Code Online (Sandbox Code Playgroud)

即,它承认声明是不同的,因此必须已实施了第1980号决议。它没有发现第二个重载是更专门的(请参阅Wandbox链接),但这并不一致,因为它应该已经根据(8.2)中的上述约束诊断了您的代码。

可以说,当前的措词可以使您的示例按要求进行部分排序[temp.deduct.type] / 1提到从类型中推论,

可以在几种不同的上下文中推导出模板参数,但是在每种情况下,都会将根据模板参数指定的类型(称为P)与实际类型(称为A)进行比较,并尝试查找模板参数值[ ...],这将使P推导出的值代之后(称之为推断A),兼容A

现在,通过[temp.alias] / 3,这意味着在部分排序的步骤(其中部分专业化的功能模板是参数模板)中,替换为is_samefalse(因为公共库实现仅使用必须失败的部分专业化) ,并且enable_if失败。但是,这种语义在一般情况下并不令人满意,因为我们可以构造一个通常会成功的条件,因此唯一的综合类型会同时满足它,并且推论会双向成功。

大概,最简单,最可靠的解决方案是在部分排序期间忽略丢弃的参数(使您的示例格式错误)。在这种情况下(类似于发行号1157),也可以使自己适应实现的行为:

template <typename...> struct C {};

template <typename T>
void f( C<T, int> ) = delete;

template <typename T>
void f( C<T, std::enable_if_t<sizeof(T) == sizeof(int), int>> ) {}

int main() {f<int>({});}
Run Code Online (Sandbox Code Playgroud)

无论GCC诊断这是调用删除功能,即同意第一个重载更专业比其他。#2的关键属性似乎是第二个模板参数是从属的,但T仅在非推论上下文中出现(如果我们在#1中更改intT,则什么也没有改变)。因此,我们可以使用丢弃的(和相关的?)模板参数作为平局决胜局:通过这种方式,我们不必推理合成值的性质(即现状),并且在您的情况下也可以获得合理的行为,这将是格式正确的。


@TC提到,通过[temp.class.order]生成的模板当前将被解释为一个已多重声明的实体-再次参见第1980期。在这种情况下,这与标准语言没有直接关系,因为该措辞从未提及已声明了这些功能模板,更不用说在同一程序中了。它只是指定它们,然后退回到功能模板的过程。

尚不清楚执行此分析需要什么深度的实现。问题1157演示了“正确”确定模板域是否是其他域的适当子集所需的详细程度。实现这种复杂的部分排序既不切实际,也不合理。但是,带脚注的部分只是表明该主题不一定未指定,而是有缺陷的。


Mac*_*cki 1

我认为你可以简化你的代码 - 这与 type_traits 无关。您将得到与以下结果相同的结果:

template <typename T>
struct C;

template<typename T>
using first = T;

template <typename T>
struct C<first<T>>  // OK only in 5.1
{
};

int main ()
{
}
Run Code Online (Sandbox Code Playgroud)

检查在线编译器(在 5.1 下编译,但不在 5.2 或 4.9 下编译,所以这可能是一个错误) - https://godbolt.org/g/iVCbdm

我认为在 GCC 5 中,他们围绕模板功能进行了移动,甚至可以创建相同类型的两个专业化。它将编译直到您尝试使用它。

template <typename T>
struct C;

template<typename T1, typename T2>
using first = T1;

template<typename T1, typename T2>
using second = T2;

template <typename T>
struct C<first<T, T>>  // OK on 5.1+
{
};

template <typename T>
struct C<second<T, T>>  // OK on 5.1+
{
};

int main ()
{
   C<first<int, int>> dummy; // error: ambiguous template instantiation for 'struct C<int>'
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/g/6oNGDP

它可能与增加对 C++14 变量模板的支持有关。https://isocpp.org/files/papers/N3651.pdf