反正是否基于C ++中的参数成员专门化模板?

时间:2012-06-19 19:05:06

标签: c++ templates template-specialization template-meta-programming

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

2 个答案:

答案 0 :(得分:8)

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

接下来是使用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解决了这个问题。