有没有专门化这样的模板,只有在T有成员函数hash
时才能使用专门化? (注意这只是我想要做的一个例子。我知道每个具有hash
函数的类在operator==
成员函数中自行检查它会更有意义,但我只是想知道这种事情是否可行。)
template <class T>
bool equals(const T &x, const T &y)
{
return x == y;
}
template <class T> // somehow check if T has a member function 'hash'
bool equals<T>(const T &x, const T &y)
{
return x.hash() == y.hash() && x == y;
}
如果可能的话,我更喜欢预先使用C ++ 11解决方案。
答案 0 :(得分:8)
这是我自己的代码中的一个例子。正如您可能从其中一个结构名称中猜到的那样,这是基于Substitution Failure is Not an Error的原则。结构has_member_setOrigin
定义了test
的两个版本。如果U
没有成员setOrigin
,则无法满足第一个要求。由于在模板替换中不是错误,因此它就好像它不存在一样。因此,多态函数的分辨率顺序找到test(...)
,否则它将具有较低的优先级。然后,value
由test
的返回类型确定。
接下来是使用callSetOrigin
模板的equals
(相当于您的enable_if
)的两个定义。如果检查enable_if
,您会看到如果第一个模板参数为true,则定义enable_if<...>::type
,否则定义callSetOrigin
。这再次在template <typename V>
struct has_member_setOrigin
{
template <typename U, void (U::*)(const Location &)> struct SFINAE {};
template <typename U> static char test(SFINAE<U, &U::setOrigin>*);
template <typename U> static int test(...);
static const bool value = sizeof(test<V>(0)) == sizeof(char);
};
template<typename V>
void callSetOrigin(typename enable_if <has_member_setOrigin<V>::value, V>::type &p, const Location &loc) const
{
p.setOrigin(loc);
}
template<typename V>
void callSetOrigin(typename enable_if <!has_member_setOrigin<V>::value, V>::type &p, const Location &loc) const
{
}
的一个定义中创建了替换错误,只有一个存活。
enable_if
忘了我也提供了#ifndef __ENABLE_IF_
#define __ENABLE_IF_
template<bool _Cond, typename _Tp>
struct enable_if
{ };
template<typename _Tp>
struct enable_if<true, _Tp>
{ typedef _Tp type; };
#endif /* __ENABLE_IF_ */
的定义:
{{1}}
答案 1 :(得分:2)
C ++ 11解决方案。只需修改返回类型,使其只有.hash
的有效类型。此外,我们使用逗号运算符,这样,虽然编译器会检查它是否可以计算declval<T>.hash()
,但它实际上会忽略它并使用true
的类型,这是当然是bool
,你想要的类型。
template <class T>
auto equals(const T &x, const T &y) -> decltype(declval<T>.hash(), true)
{
return x.hash() == y.hash() && x == y;
}
我相信这叫做表达SFINAE。
更多详情:
decltype(X,Y)
与decltype(Y)
相同(感谢逗号运算符)。这意味着我的返回类型基本上是decltype(true)
,即bool
,根据需要。那我为什么要declval<T>.hash()
?这不仅仅是浪费空间吗?
答案是T
具有hash
方法的测试。如果此测试失败,则返回类型的计算失败,因此该函数不会被视为有效的重载,编译器将查找其他位置。
最后,如果您之前没有见过declval
,那么在未评估的上下文(例如T
)中创建decltype
类型的对象是一种非常有用的方法。您可能想写T()
来构建T
,因此使用T().hash()
来调用hash
。但是,如果T
没有默认构造函数,那将无效。 declval
解决了这个问题。