c++ - 为什么我的 SFINAE 表达式不再适用于 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;
}
旧版本的 GCC(不幸的是我不记得我之前安装的确切版本)以及 Clang 编译上面的代码就好了,但是 GCC 8.2 给出了一个错误说明:
:在函数'int main()'中: :29:19: 错误: 重载 'test()' 的调用不明确 c.test(); ^ :12:10: 注意:候选人:'void Class::test() [with U = int&; 类型名 std::enable_if::type>::value>::type ... = {}]' 无效测试(){ ^~~~ :22:10: 注意:候选人:'void Class::test() [with U = int&; 类型名 std::enable_if::type>::value)>::type ... = {}]' 无效测试(){ ^~~~ :30:25: 错误: 重载 'test()' 的调用不明确 c.test(); ^ :12:10: 注意:候选人:'void Class::test() [with U = const int&; 类型名 std::enable_if::type>::value>::type ... = {}]' 无效测试(){ ^~~~ :22:10: 注意:候选人:'void Class::test() [with U = const int&; 类型名 std::enable_if::type>::value)>::type ... = {}]' 无效测试(){
通常情况下,当不同的编译器和编译器版本以不同的方式处理相同的代码时,我假设我正在调用未定义的行为。标准对上述代码有什么说法?我究竟做错了什么?
注意:问题不在于解决此问题的方法,我想到了几种方法。问题是为什么这不适用于 GCC 8 - 它是标准未定义的,还是编译器错误?
注意 2:由于每个人都在使用默认void
类型std::enable_if
,因此我已将问题更改为使用int
。问题依然存在。
注 3: 已创建 GCC 错误报告
解决方案
这是我的看法。总之,clang是对的,gcc有回归。
我们有根据[temp.deduct]p7:
替换发生在函数类型和模板参数声明中使用的所有类型和表达式中。[...]
这意味着无论包是否为空,都必须进行替换。因为我们仍然处于直接上下文中,所以这是 SFINAE-able。
接下来我们知道可变参数确实被认为是实际的模板参数;来自[temp.variadic]p1
模板参数包是接受零个或多个模板参数的模板参数。
并且[temp.param]p2表示允许哪些非类型模板参数:
非类型模板参数应具有以下类型之一(可选 cv 限定):
一种文字类型,具有强结构相等性([class.compare.default]),没有可变或易变的子对象,并且如果存在默认成员运算符<=>,则将其声明为公共的,
左值引用类型,
包含占位符类型 ([dcl.spec.auto]) 的类型,或
推导类类型的占位符 ([dcl.type.class.deduct])。
请注意,void
这不符合要求,您的代码(如发布的)格式不正确。
推荐阅读
- python - imread 在某些图像上返回 None ,但在同一文件夹中的其他图像上不返回
- actions-on-google - 帐户关联完成后,Google 操作“现在没有响应”
- c# - 如何使用 Visual Studio 加载 JSON 类架构
- php - 通过 JQuery 将数据表中的复选框数据发送到 php 文件
- arm - 如何为 ARM Linux 设备交叉构建 luajit-5.1?(尝试构建 LÖVE 框架)
- kubernetes - 允许使用 oc/kubectl 端口转发的角色的名称是什么?
- c# - 从 transaction.Commit() 中检索 SQL 错误消息
- ionic3 - Cognito 和 Ionic 3 - 让用户保持登录状态
- android - Codenameone - Android平板电脑上的应用程序崩溃并从下一次打开一个空白的黑屏
- sql - 从超过一年前的 SQL 数据库中搜索日期