SFINAE:检测成员变量的存在在 g++ 上不起作用
SFINAE: Detecting existence of member variable does not work on g++
我正在尝试将 this answer for detecting whether a class has a member variable x
with 中使用的方法结合到 select 不同的实现中,具体取决于使用 enable_if
.
基本上,我想写一个特征 class,给定一个类型 T
,如果它存在,则提供对成员 T::x
的访问,否则提供默认值。
以下代码无法在 g++ 上编译:(Compiler Explorer)
#include <iostream>
#include <type_traits>
// classes with / without x member
struct WithX { static constexpr int x = 42; };
struct WithoutX {};
// trait to detect x
template <typename T, typename = void>
struct HasX : std::false_type { };
template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type { };
// trait to provide default for x
template <typename T>
struct FooTraits
{
template <bool enable = HasX<T>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <bool enable = HasX<T>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }
};
int main() {
std::cout << HasX<WithX>::value << std::endl;
// Uncomment the following line to make this compile with g++
//std::cout << HasX<WithoutX>::value << std::endl;
std::cout << FooTraits<WithoutX>::x() << std::endl;
}
g++ 给出了
的错误消息
error: 'x' is not a member of 'WithoutX'
struct HasX <T, decltype((void) T::x)> : std::true_type { };
在首先应该检测 x
是否是成员的部分。奇怪的是,如果我取消注释倒数第二行实例化 HasX<WithoutX>::value
本身,g++ 编译没有错误 (Compiler Explorer).
clang 和 msvc 在 Compiler Explorer 上编译都没有问题。
这是怎么回事?
SFINAE 仅适用于直接上下文。也就是说,如果编译器能提前看出某个声明有问题,那么一定是错误。
实例化 class 时,编译器将尝试解析它可以解析的所有内容。这也是:
template<bool enable = HasX<T>::value>
....
这与函数的上下文无关。这可以在实例化 FooTraits
时实例化。
换句话说,这个分配可以提前计算,就好像你把它移动到class范围。
在你的情况下,编译器在替换时没有任何作用。
解决方法很简单:
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }
理论上,U
可以是完全不同的类型。 U
直接到函数实例化,而 T
不是。
切换评论的事实:
//std::cout << HasX<WithoutX>::value << std::endl;
确实是 gcc bug 的好兆头。
gcc 似乎对您的表单有问题:
template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type {};
一个更典型的方法是使用std::void_t
:
template <typename T>
struct HasX <T, std::void_t<decltype(T::x)>> : std::true_type {};
这确实解决了问题 Demo。
我正在尝试将 this answer for detecting whether a class has a member variable x
with enable_if
.
基本上,我想写一个特征 class,给定一个类型 T
,如果它存在,则提供对成员 T::x
的访问,否则提供默认值。
以下代码无法在 g++ 上编译:(Compiler Explorer)
#include <iostream>
#include <type_traits>
// classes with / without x member
struct WithX { static constexpr int x = 42; };
struct WithoutX {};
// trait to detect x
template <typename T, typename = void>
struct HasX : std::false_type { };
template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type { };
// trait to provide default for x
template <typename T>
struct FooTraits
{
template <bool enable = HasX<T>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <bool enable = HasX<T>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }
};
int main() {
std::cout << HasX<WithX>::value << std::endl;
// Uncomment the following line to make this compile with g++
//std::cout << HasX<WithoutX>::value << std::endl;
std::cout << FooTraits<WithoutX>::x() << std::endl;
}
g++ 给出了
的错误消息error: 'x' is not a member of 'WithoutX'
struct HasX <T, decltype((void) T::x)> : std::true_type { };
在首先应该检测 x
是否是成员的部分。奇怪的是,如果我取消注释倒数第二行实例化 HasX<WithoutX>::value
本身,g++ 编译没有错误 (Compiler Explorer).
clang 和 msvc 在 Compiler Explorer 上编译都没有问题。
这是怎么回事?
SFINAE 仅适用于直接上下文。也就是说,如果编译器能提前看出某个声明有问题,那么一定是错误。
实例化 class 时,编译器将尝试解析它可以解析的所有内容。这也是:
template<bool enable = HasX<T>::value>
....
这与函数的上下文无关。这可以在实例化 FooTraits
时实例化。
换句话说,这个分配可以提前计算,就好像你把它移动到class范围。
在你的情况下,编译器在替换时没有任何作用。
解决方法很简单:
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t< enable, size_t> x() { return T::x; }
template <typename U = T, bool enable = HasX<U>::value>
static constexpr std::enable_if_t<!enable, size_t> x() { return 1; }
理论上,U
可以是完全不同的类型。 U
直接到函数实例化,而 T
不是。
切换评论的事实:
//std::cout << HasX<WithoutX>::value << std::endl;
确实是 gcc bug 的好兆头。
gcc 似乎对您的表单有问题:
template <typename T>
struct HasX <T, decltype((void) T::x)> : std::true_type {};
一个更典型的方法是使用std::void_t
:
template <typename T>
struct HasX <T, std::void_t<decltype(T::x)>> : std::true_type {};
这确实解决了问题 Demo。