如何将enable_if用于互斥的非成员函数模板?

时间:2012-06-02 11:38:12

标签: c++ templates c++11 sfinae

我试图编写非成员运算符函数模板,如:

#include <utility>

template < typename T, unsigned L >
class MyType;

template < typename T, typename U, unsigned L >
auto  operator ==( MyType<T,L> const &l, MyType<U,L> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

但是当我尝试处理lr的长度不同时:

template < typename T, unsigned Lt, typename U, unsigned Lu, class Enable = typename std::enable_if<(Lt < Lu)>::type >
auto  operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

template < typename T, unsigned Lt, typename U, unsigned Lu, class Enable = typename std::enable_if<(Lt > Lu)>::type >
auto  operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

我出现歧义错误。我试过像:

template < typename T, unsigned Lt, typename U, unsigned Lu, bool B = (Lt < Lu), class Enable = typename std::enable_if<B>::type >
auto  operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

template < typename T, unsigned Lt, typename U, unsigned Lu, bool B = (Lt > Lu), class Enable = typename std::enable_if<B>::type >
auto  operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

我已经读过(这里是S.O.)为成员函数模板解决这样的问题。 (有时,受访者将成员函数更改为成员函数模板以启用此功能。)但错误不会对我有所改变。我是否必须切换到将enable_if放入返回类型?

哦,当两种元素类型无法比较时,返回类型表达式应该排除此运算符。它真的有效吗?是否兼容将enable_if放在那里?

1 个答案:

答案 0 :(得分:11)

有趣的是,certain fellow here on SO在不久前写了一篇博客文章,展示了一种很好的C ++ 11风格的SFINAE技术,它可以轻松实现重载功能。提供了技术和解释here

简而言之,您的代码会失败,因为在第一次解析时和解析非依赖声明时,两个模板完全相同,类型方式。与默认函数参数一样,默认模板参数仅在实际调用函数时被替换。这就是两个模板在声明时对编译器的看法:

template<class T, unsigned Lt, class U, unsigned Lu, class Enable>
auto operator==(MyType<T,Lt> const& l, MyType<U,Lu> const& r);

以下代码应达到您的目的:

namespace detail{
enum class enabler{};
}

template<bool B, class T = detail::enabler>
using EnableIf = typename std::enable_if<B, T>::type;

template < typename T, unsigned Lt, typename U, unsigned Lu, EnableIf<(Lt < Lu)>...>
auto  operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

template < typename T, unsigned Lt, typename U, unsigned Lu, EnableIf<(Lt > Lu)>...>
auto  operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
 -> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }

然而,还有一个问题...... Lt == Lu会发生什么?在这种情况下,过载都不可行。