我在C ++中遇到了Member Detector成语,这是一个类型特征,用于判断一个类是否包含某个名称的成员。但是如果类型不是类,链接的示例不能像我预期的那样工作:我想要任何非类类型的false
结果。一个可能的解决方案肯定是这样的,使用boost::is_class<T>
:
template<typename T>
struct general_DetectX : boost::mpl::and_<
boost::is_class<T>,
DetectX<T> >::type
{ };
bool hasX = general_DetectX<int>::value; // hasX = false
但是这个问题是为什么原DetectX<T>
会产生错误而不是做SFINAE事情。以下是链接代码相关部分的摘录(本地结构Fallback
和Check<U,U>
以及typedef ArrayOfOne
,ArrayOfTwo
和type
为了简洁而删除了以下内容:
template<typename T>
class DetectX
{
struct Derived : T, Fallback { };
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &U::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
enum { value = sizeof(func<Derived>(0)) == 2 };
};
可以看出DetectX::Derived
在任何重载决策之外使用,因此永远不会调用处理错误的SFINAE规则。但是,这可以更改为使用Derived
作为重载解析的一部分发生的地方:
template<typename T>
class DetectX
{
template<typename U>
struct Derived : U, Fallback { };
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &Derived<U>::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
enum { value = sizeof(func<T>(0)) == 2 };
};
Derived<T>
模板仅在尝试实例化第一个func()
重载时实例化,那么为什么即使修改后的版本仍然会出错?这是一个例子:
$ g++ --version | head -n1
g++ (GCC) 4.8.2
$ g++ -c demo.cxx
demo.cxx: In instantiation of 'struct DetectX<int>::Derived<int>':
demo.cxx:16:53: required by substitution of 'template<class U> static char (& DetectX<T>::func(DetectX<T>::Check<int DetectX<T>::Fallback::*, (& DetectX<T>::Derived<U>::X)>*))[1] [with U = U; T = int] [with U = int]'
demo.cxx:24:31: required from 'class DetectX<int>'
demo.cxx:27:25: required from here
demo.cxx:7:12: error: base type 'int' fails to be a struct or class type
struct Derived : U, Fallback { };
^
答案 0 :(得分:3)
您可以使用:(https://ideone.com/LArNVO)
#include <cstdint>
#include <type_traits>
#define DEFINE_HAS_MEMBER(traitsName, memberName) \
template <typename U> \
class traitsName \
{ \
private: \
struct Fallback { int memberName; }; \
struct Dummy {}; \
template<typename T, bool is_a_class = std::is_class<T>::value> \
struct identity_for_class_or_dummy { using type = Dummy; }; \
template<typename T> \
struct identity_for_class_or_dummy<T, true> { using type = T; }; \
\
template <typename Base> \
struct Derived : Base, Fallback {}; \
template<typename T, T> struct helper; \
template<typename T> \
static std::uint8_t \
check(helper<int (Fallback::*), \
&Derived<typename identity_for_class_or_dummy<T>::type>::memberName>*); \
template<typename T> static std::uint16_t check(...); \
public: \
static \
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint16_t); \
}
DEFINE_HAS_MEMBER(has_foo, foo);
// Now test it:
class C{ public: int foo; };
class D : public C {};
class E {};
static_assert(has_foo<C>::value, "");
static_assert(has_foo<D>::value, "");
static_assert(!has_foo<E>::value, "");
static_assert(!has_foo<int>::value, "");
您可以在以下问题What is exactly the “immediate context” mentioned in the C++11 Standard for which SFINAE applies?
中理解为什么它会失败简而言之,SFINAE适用于Derived<U>::X
但不适用Derived<U>
(在您的情况下形成不良)。