【发布时间】:2019-01-18 03:22:24
【问题描述】:
我最近将 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。问题依然存在。
【问题讨论】:
-
从 godbolt 开始,它一直工作到
gcc 7.3(你可以在程序集中看到它做了正确的事情)。 -
在
::type之后用...扩展什么? -
删除省略号,将默认的
void替换为int并添加默认值可以解决问题,是的。真正的问题是,上述代码以前运行良好,但在 GCC 8 中不再运行的原因是什么? -
@xskxzr 根据这个问题,
void...仅适用于空参数包,这正是这里所需要的。即使它是非法的,在std::enable_if中用int替换默认的void也不会改变问题的任何内容。 -
最小示例:godbolt.org/g/P9z1pt gcc7.1 OK gcc 8.x KO
标签: c++ c++11 gcc language-lawyer sfinae