enable_if我通常更喜欢按照您的尝试创建自定义样式类型,因为我发现使用单个特征类型而不是enable_if<some_trait<T>, another_trait<T>>. 但是在这种情况下,您的代码中存在一些问题,使其无法正常工作。
enable_if_has_bar永远不会选择您的专业化,返回类型ReturnBar只是实例化主模板,enable_if_has_bar<foo, void>并且从不定义嵌套的type. 没有什么会导致特化被实例化,所以没有任何东西可以检查是否T::bar是一个有效的表达式。
你的decltype(static_cast<T*>(0)->*static_cast<decltype(&T::bar)>(0))表达将导致int&不是int你可能想要的。这是因为decltype(foobar.*(&foo::bar))is 等价于decltype(foobar.bar)andfoobar.bar是一个左值,所以decltypeis int&。ReturnBar如果返回该函数将无法编译,int&因为参数value是 const,因此您不能绑定value.bar到非 const int&。
这是一个工作版本:
template <class T>
class has_bar
{
template<typename U, typename = decltype(&U::bar)>
static std::true_type
test(U*);
static std::false_type
test(...);
public:
static const int value = decltype(test((T*)nullptr))::value;
};
template<typename T, bool = has_bar<T>::value>
struct enable_if_has_bar
{ };
template<typename T>
struct enable_if_has_bar<T, true>
: std::decay<decltype(std::declval<T&>().*(&T::bar))>
{ };
这首先声明了帮助has_bar器来回答类型是否具有嵌套成员的问题。该助手使用 SFINAE 来获取true或false值。if&T::bar是一个有效的表达式,那么将使用第一个重载 of test,它返回true_type,因此value将设置为true_type::valueie true。否则,将选择后备过载并将其value设置为false。
然后enable_if_has_bar模板使用默认模板参数,该参数被推导出为 的值has_bar<T>::value。has_bar<T>::value为 false时使用主模板。当has_bar<T>::value为真时使用特化,在这种情况下我们知道表达式&T::bar是有效的并且可以在 decltype 表达式中使用它来获取类型。
std::decay用于将int&decltype 表达式的结果转换为 just int。继承自decay比使用它定义成员要短一些,即:
typedef typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type type;
我在未计算的表达式中使用了标准实用程序declval<T>(),它比static_cast<T*>(0).
使用的替代方法decay是另一种帮助类型int从类型中获取类型,int T::*例如
template<typename T>
struct remove_class;
{ };
template<typename Member, typename Class>
struct remove_class<Member Class::*>
{
typedef Member type;
};
template<typename T>
struct enable_if_has_bar<T, true>
: remove_class<decltype(&T::bar)>
{ };
(这个名字remove_class不是很好,但基本上它需要一个指向数据成员的指针类型并给出成员的类型。)