为什么void_t <>检测成语不适用于gcc-4.9?

Dav*_*man 14 c++ templates g++ sfinae c++14

请考虑以下代码:

#include <iostream>
#include <type_traits>

struct Test { Test& operator++(); };
struct NoIncrement { };

template <typename...> using void_t = void;

template <class, class=void_t<>>
struct has_pre_increment_member : std::false_type { };

template <class T>
struct has_pre_increment_member<T, void_t<decltype( ++std::declval<T&>() )>>
  : public std::true_type { };

int main() {
  std::cout << has_pre_increment_member<Test>::value << " ";
  std::cout << has_pre_increment_member<NoIncrement>::value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

使用g ++版本5及更高版本(当然还有-std = c ++ 14标志),此代码输出

1 0
Run Code Online (Sandbox Code Playgroud)

正如它应该.但是,使用g ++版本4.9(以及-std = c ++ 14标志),它会输出

1 1
Run Code Online (Sandbox Code Playgroud)

两者都声称使用相同的语言标准,那么这里的问题是什么?

Bar*_*rry 20

这是CWG Issue 1558的结果,现在被认为是gcc中的一个错误(特别是64395 - 目前已修复).这个问题背后的想法是,因为你实际上并没有在这里使用模板参数:

template <typename...> using void_t = void;
Run Code Online (Sandbox Code Playgroud)

无论您尝试传递什么类型或表达,都没有替换失败.

值得庆幸的是,有一个简单的解决方法,不涉及升级您的编译器.我们可以重写void_t实际使用它的参数包,从而触发替换失败:

namespace void_details {
    template <class... >
    struct make_void { using type = void; };
}

template <class... T> using void_t = typename void_details ::make_void<T...>::type;
Run Code Online (Sandbox Code Playgroud)

这将使你的例子在我尝试的所有gcc版本中做正确的事情.