为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
Why do my SFINAE expressions no longer work with GCC 8.2?
我最近将GCC升级到8.2,我的大部分SFINAE表达式都停止工作了。
以下内容略有简化,但演示了问题:
#include <iostream>
#include <type_traits>
class Class {
public:
template <
typename U,
typename std::enable_if<
std::is_const<typename std::remove_reference<U>::type>::value, int
>::type...
>
void test() {
std::cout << "Constant" << std::endl;
}
template <
typename U,
typename std::enable_if<
!std::is_const<typename std::remove_reference<U>::type>::value, int
>::type...
>
void test() {
std::cout << "Mutable" << std::endl;
}
};
int main() {
Class c;
c.test<int &>();
c.test<int const &>();
return 0;
}
C++ (gcc( – 在线试用
C++ (叮当声( – 在线试用
旧版本的GCC(不幸的是我不记得我以前安装的确切版本(以及Clang编译上述代码就可以了,但是GCC 8.2给出了一个错误,指出:
:在函数 'int main((' 中: :29:19:错误:重载的"test(("调用不明确 c.test((; ^ :12:10: 注意: 候选: 'void Class::test(( [与 U = int&; typename std::enable_if::type>::value>::type ... = {}]' void test(( { ^~~~ :22:10: 注意: 候选: 'void Class::test(( [与 U = int&; typename std::enable_if::type>::value(>::type ... = {}]' void test(( { ^~~~ :30:25:错误:重载的"test(("调用不明确 c.test((; ^ :12:10: 注意: 候选: 'void Class::test(( [与 U = const int&; typename std::enable_if::type>::value>::type ... = {}]' void test(( { ^~~~ :22:10: 注意: 候选: 'void Class::test(( [与 U = const int&; typename std::enable_if::type>::value(>::type ... = {}]' void test(( {
当不同的编译器和编译器版本以不同的方式处理相同的代码时,通常会出现这种情况,我假设我正在调用未定义的行为。标准对上述代码有什么看法?我做错了什么?
注意:问题不在于解决此问题的方法,而是想到了几个问题。问题是为什么这不适用于 GCC 8 - 它是标准未定义的,还是编译器错误?
注意2:由于每个人都跳上默认void
类型的std::enable_if
,我已将问题更改为使用int
。问题依然存在。
注3:已创建GCC错误报告
这是我的看法。简而言之,clang是对的,gcc有一个回归。
根据 [温度扣除]p7,我们有:
替换发生在函数类型和模板参数声明中使用的所有类型和表达式中。[...]
这意味着无论包装是否为空,都必须进行替换。因为我们仍然处于直接的背景下,所以这是可以的。
接下来,我们得到一个可变参数确实被认为是一个实际的模板参数;来自 [temp.variadic]p1
模板参数包是接受零个或多个模板参数的模板参数。
和 [temp.param]p2 表示允许哪些非类型模板参数:
非类型模板参数应具有以下类型之一(可选符合 cv 条件(:
文字
类型,具有很强的结构相等性([class.compare.default](,没有可变或易失性的子对象,并且如果存在默认的成员运算符 <=>,则声明为 public,
左值引用类型,
包含占位符类型 ([dcl.spec.auto]( 的类型,或
推导类类型的占位符 ([dcl.type.class.deduct](。
请注意,void
不符合要求,则您的代码(已发布(格式不正确。
我不是语言律师,但以下引用是否与问题有某种联系?
[temp.deduct.type/9]:如果 Pi 是一个包扩展,则 Pi 的模式与 A 的模板参数列表中的每个剩余参数进行比较。每次比较都会推断出由 Pi 扩展的模板参数包中后续位置的模板参数。
在我看来,由于模板参数列表中没有剩余的参数,因此没有模式的比较(包含enable_if
(。如果没有比较,那么也没有扣除,我相信扣除后会发生替换。因此,如果没有替代,则不应用 SFINAE。
如果我错了,请纠正我。我不确定这一特定段落是否适用于这里,但在 [temp.deduct] 中有更多关于包装扩展的类似规则。此外,此讨论可以帮助更有经验的人解决整个问题:https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/JwZiV2rrX1A。
部分答案:使用具有不同T
的typename = typename enable_if<...>, T=0
:
#include <iostream>
#include <type_traits>
class Class {
public:
template <
typename U,
typename = typename std::enable_if_t<
std::is_const<typename std::remove_reference<U>::type>::value
>, int = 0
>
void test() {
std::cout << "Constant" << std::endl;
}
template <
typename U,
typename = typename std::enable_if_t<
!std::is_const<typename std::remove_reference<U>::type>::value
>, char = 0
>
void test() {
std::cout << "Mutable" << std::endl;
}
};
int main() {
Class c;
c.test<int &>();
c.test<int const &>();
return 0;
}
(演示(
仍然试图弄清楚知道默认类型是void
std::enable_if<...>::type...
到底是什么意思。
- 为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
- 适用于 gcc 4.8 的任何正则表达式库
- 流输入运算符导致分段错误与 clang 但适用于 gcc
- -static-libstdc++ 适用于 g++ 但不适用于纯 gcc?
- enable_if的模板专用化在 Clang 中失败,适用于 GCC
- 函数指针数组的类模板参数推导适用于 clang,但不适用于 gcc
- 可变参数模板作为模板参数:演绎适用于 GCC,但不适用于 Clang
- -m32 选项不适用于 gcc,但适用于 g++
- 在哪里可以下载适用于 Windows 的 GCC 4.3.2 二进制文件
- gcc-fdiagnostics颜色是否适用于Windows
- 将 C++11 正则表达式与 gcc 4.8.2 一起使用时会出现奇怪的结果(但适用于 Boost 正则表达式)
- Netbeans 8.1(适用于 C/C++)找不到我的编译器(gcc-6.0.0 开发版本)
- 在 Linux 上使用 GCC 交叉编译适用于 Windows 的 Qt 应用程序
- 宏调用中的 #ifdef 适用于 GCC,但不适用于 MSVC
- 静态表生成适用于 GCC,但不适用于 clang;被叮当窃听
- 用于提升program_options的自定义验证器不适用于 GCC,适用于 MSVC
- 模板的类型定义包括char[][] - 适用于VS2008,但不适用于gcc
- 如何构建独立于编译器的C++库(适用于 Solaris Studio 和 gcc)
- GCC 奇怪的链接器错误.适用于Visual Studio 2010
- 模板专用化适用于 gcc,但不适用于 Visual Studio 10