为什么 void_t<> 检测习惯用法不适用于 gcc-4.9?
Why does the void_t<> detection idiom not work with gcc-4.9?
考虑以下代码:
#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;
}
对于 g++ 版本 5 及更高版本(当然还有 -std=c++14 标志),此代码输出
1 0
这是应该的。然而,对于 g++ 4.9 版(和 -std=c++14 标志),它输出
1 1
两者都声称使用相同的语言标准,所以这里有什么问题?
这是 CWG Issue 1558, and is now considered a bug in gcc (specifically 64395 的结果 - 目前已修复)。问题背后的想法是,由于您实际上并未在此处使用模板参数:
template <typename...> using void_t = void;
无论您尝试传入什么类型或表达式,都不会出现替换失败。
值得庆幸的是,有一个不涉及升级编译器的简单解决方法。我们可以重写 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;
这将使您的示例在我尝试过的所有 gcc 版本中都能正确执行。
考虑以下代码:
#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;
}
对于 g++ 版本 5 及更高版本(当然还有 -std=c++14 标志),此代码输出
1 0
这是应该的。然而,对于 g++ 4.9 版(和 -std=c++14 标志),它输出
1 1
两者都声称使用相同的语言标准,所以这里有什么问题?
这是 CWG Issue 1558, and is now considered a bug in gcc (specifically 64395 的结果 - 目前已修复)。问题背后的想法是,由于您实际上并未在此处使用模板参数:
template <typename...> using void_t = void;
无论您尝试传入什么类型或表达式,都不会出现替换失败。
值得庆幸的是,有一个不涉及升级编译器的简单解决方法。我们可以重写 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;
这将使您的示例在我尝试过的所有 gcc 版本中都能正确执行。