应根据C 标准进行以下代码编译

Should the following code compile according to C++ standard?

本文关键字:代码 编译 标准 应根据      更新时间:2023-10-16
#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 ()
{
}

不同编译器编译的结果:

msvc:

错误C2753:'C':部分专业化无法匹配主模板的参数列表

GCC-4.9:

错误:部分专业化'c'不专业化任何模板参数

所有版本:

错误:类模板部分专业化并不专业化任何模板参数;要定义主模板,请删除模板参数列表

GCC-5 :成功编译

和其他我想指出的是:

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

成功未能由GCC编译。因此,似乎已经确定了我原始示例中的专业化是非平凡的。所以我的问题是 - 是否标准是否明确禁止这样的模式?

关键段落是[temp.class.spec]/(8.2),这要求部分专业化比主要模板更专业。Clang实际上抱怨的是,参数列表与主要模板相同:这已从第2033号问题中从[temp.class.spec]/(8.3)中删除(表明最近的要求是冗余的),因此hasn'hasn'T在Clang中实施。但是,鉴于它接受了您的片段,因此显然已经在海湾合作委员会中实施了。它甚至会编译以下内容,也许是出于同样的原因编译您的代码(也只能从版本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>>> ) {}

即。它承认声明是不同的,因此必须已经实现了1980年问题的某些解决方案。但是,它并未发现第二个过载更加专业化(请参阅wandbox链接),但是,这是不一致的,因为它应该诊断出您的代码已经诊断出您的代码。根据(8.2)中的上述约束。

可以说,当前的措辞使您的示例的部分订购按要求的&Dagger; :[temp.deduct.type]/1提及从类型扣除时,

模板参数可以在几种不同的上下文中推导,但是在每种情况下,都将根据模板参数(调用P)指定的类型与实际类型(称为A)进行比较,并尝试尝试进行尝试。查找模板参数值[…]将使P替换为推论值(称其为推论A),与A兼容。

现在通过[temp.alias]/3,这意味着在部分订购步骤中,部分专业化函数的函数模板是参数模板,替换为 is_same会产生false(因为公共库实现仅使用部分专业化必须失败),而enable_if失败了。&Dagger; ,但是在一般情况下,这种语义并不令人满意,因为我们可以构建一个通常成功的条件,因此独特的合成类型可以满足它,并且推论成功了两种方式。

大概,最简单,最强大的解决方案是忽略部分订购期间被丢弃的参数(使您的示例不正确)。在这种情况下,也可以将自己定为实施行为(类似于第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>({});}

clang和GCC都诊断为调用已删除功能,即同意第一个超载比另一个过载更专业。#2的关键属性似乎是第二个模板参数取决于T仅出现在非伪造的上下文中(如果我们在#1中将int更改为T,则没有任何更改)。因此,我们可以将废弃(和依赖的?)模板参数的存在作为打决我们:这样我们就不必推理合成价值的性质,这是现状,并且在您的情况下也获得了合理的行为,这将是很好的。


&Dagger; @t.c。提到,通过[temp.class.order]生成的模板当前将被解释为一个乘积声明的实体;再次,请参见第1980期。在这种情况下,这与标准人无关,因为措辞永远不会提到这些功能模板是宣布,更不用说在同一计划中了;它只是指定了它们,然后又回到功能模板的过程中。

&Dagger; 对执行此分析所需的深度实现并不完全清楚。问题1157证明了"正确"确定模板域是否是对方的适当子集需要的细节级别。实施部分命令成为这种复杂性既实用也不合理。但是,脚注部分只是表明该主题不一定被指定,而是有缺陷。

我认为您可以简化代码 - 这与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 ()
{
}

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

我认为他们围绕模板功能移动了Int 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>'
}

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

它可能以某种方式与对C 14变量模板的支持相关。https://isocpp.org/files/papers/n3651.pdf