我正在尝试结合此答案中使用的方法来检测类是否具有成员变量,x
与此答案一起使用,以根据使用enable_if
的实现选择不同的实现。
基本上,我想编写一个 trait 类,给定一个类型T
,提供对成员T::x
(如果存在)的访问,否则提供默认值。
以下代码不能在 g++ 上编译:(编译器资源管理器)
#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++ 编译时没有错误(编译器资源管理器)。
clang 和 msvc 在编译器资源管理器上编译都没有问题。
这是怎么回事?
SFINAE 只能在直接上下文中工作。换句话说,如果编译器可以提前看到声明有问题,那么它一定是一个错误。
实例化类时,编译器将尝试解析它所能解决的所有问题。这也是这样的:
template<bool enable = HasX<T>::value>
....
这与函数的上下文无关。这可以在实例化FooTraits
时立即实例化。
换句话说,可以提前计算此分配,就像将其移动到类范围一样。
在您的情况下,编译器与替换无关。
解决方法很简单:
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 错误的好兆头。
似乎 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 {};
这确实解决了演示问题。