6

反正有没有专门化这样的模板,只在 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 之前的解决方案。

4

2 回答 2

8

这是我自己的代码中的一个示例。正如您可能从其中一个结构名称中猜到的那样,这是基于Substitution Failure is Not an Error的原则。该结构has_member_setOrigin定义了两个版本的test. U如果没有会员,第一个不能满足setOrigin。由于这不是模板替换中的错误,因此它就像它不存在一样。因此,多态函数的解析顺序test(...)会找到优先级较低的函数。然后value由 的返回类型确定test

接下来是使用模板的callSetOrigin(相当于你的)的两个定义。如果您检查,您会发现如果第一个模板参数为真,则定义,否则不是。这再次在其中一个定义中产生了替换错误,使得只有一个存在。equalsenable_ifenable_ifenable_if<...>::typecallSetOrigin

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_ */
于 2012-06-19T19:14:52.167 回答
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;
}

我相信这被称为 Expression SFINAE。

更多细节:

decltype(X,Y)decltype(Y)(感谢逗号运算符)相同。这意味着我在这里的返回类型基本上是decltype(true), 即bool, 如所希望的。那我为什么有declval<T>.hash()?这不就是浪费空间吗?

答案是测试Thash方法。如果此测试失败,则返回类型的计算失败,因此该函数不会被视为有效重载,编译器将寻找其他地方。

最后,如果您以前没有见过,这是在未评估的上下文(例如)declval中创建类型对象的一种非常有用的方法。您可能很想编写来构造 a ,因此使用来调用。但如果没有默认构造函数,这将不起作用。解决了这个问题。TdecltypeT()TT().hash()hashTdeclval

于 2015-02-01T19:16:49.590 回答