首页 > 解决方案 > 类专业化中的clang / gcc不一致

问题描述

我在尝试为C++17 中的自定义类专门化tuple_size/以进行结构化绑定时遇到了这个问题。tuple_element

下面的代码在 GCC 中编译,但不是在 clang 中(两个主干版本,请参见下面的链接)。

#include <type_traits>

template<typename T, typename... Ts>
using sfinae_t = T;

template<typename T, bool... Bs>
using sfinae_v_t = sfinae_t<T, typename std::enable_if<Bs>::type...>;

template <typename T>
struct Test;

template <typename T>
struct Test<sfinae_v_t<T, std::is_integral_v<T>>> {};

void f() {
    Test<int> t;
}

https://godbolt.org/z/ztuRSq

这是clang提供的错误:

<source>:13:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list

struct Test<sfinae_v_t<T, std::is_integral<T>::value>> {};

       ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1 error generated.

Compiler returned: 1

这是编译器中的错误还是上面的代码调用了一些 UB?

标签: c++templateslanguage-lawyertemplate-specializationpartial-specialization

解决方案


我在下面所说的(在OLD POST下)在一定程度上应该是正确的,但实际的问题是 SFINAE 使用错误,因此我不再确定这是 gcc 中的错误。

别名声明必须始终成功,你不能在那里 SFINAE,因为它不是类或函数声明或特化(这是有道理的,因为你不能特化别名)。如果别名声明不成功,则程序格式错误。因此,编译器可能会假设在您强制它实例化这样的模板之前,它永远不会出现别名声明不成功的情况。

因此,编译器认为sfinae_v_t<T,...>always是完全可以接受的T,因为当程序不是格式错误时,这种情况会发生。因此,它将看到,在程序不是格式错误的所有情况下,部分专业化都不会专门化,因此它会告诉您这是格式错误的。(这就是 clang 所做的)。

我不认为编译器被迫这样做。如果它没有,并且只是认为“好吧,sfinae_v_t是某种类型,无论如何。”,那么这并不明显是重新声明。所以我认为在我们实例化其中一个之前,不抛出错误并没有错。

但是当我们实例化它时,应该是我们有一个重新声明的问题,或者是由于 导致程序格式std::enable_if错误,这取决于模板参数。GCC 应该至少选择其中一个,但两者都没有。

这也绝对不适用于没有std::enable_if. 所以我仍然认为这是 GCC 中的一个错误,但我已经足够心烦意乱了,我不能再肯定地说。我只想说,有人应该将其报告为错误,并让 gcc 的人考虑一下。

旧帖子

这是 gcc 中的一个错误。该标准为我们提供了在函数模板中转换类模板的规则。如果一个类模板的功能在部分函数模板排序中位于另一个类模板之前,则它比另一个类模板更专业。

我在这里创建了函数,现在 gcc 声称调用它们是模棱两可的,因此它也不得不说类模板是同样指定的。

注意:仔细阅读标准,我脑海中的编译器同意clang。


推荐阅读