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
是一个左值,所以decltype
is 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::value
ie 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
不是很好,但基本上它需要一个指向数据成员的指针类型并给出成员的类型。)