【问题标题】:How does one use enable_if for mutually exclusive non-member function templates?如何将 enable_if 用于互斥的非成员函数模板?
【发布时间】:2012-06-02 11:38:12
【问题描述】:

我正在尝试编写非成员运算符函数模板,例如:

#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 放在那里?

【问题讨论】:

  • 歧义错误到底是什么?

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


【解决方案1】:

有趣的是,不久前,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 会发生什么?在这种情况下,这两种重载都不可行。

【讨论】:

  • 哇!我的心被震撼了。我曾考虑过多条件(使用可变参数很容易),但是......结合了如此多的技术并设法达到了这样一个句法甜蜜点。惊人的。感谢您分享文章。
  • 第一个块中operator == 的版本涵盖了类模板的第二个参数相等的情况。我要问的两个版本是对第一个版本的补充,而不是替换它。
  • 那是一篇非常有趣的文章。对于我的代码,我使用typename std::enable_if&lt;(Lt &lt; Lu)&gt;::type... 作为最终模板参数。我的编译器 GCC-4.7(32 位 PowerPC,来自 MacPorts)接受了该参数,尽管它在技术上是 void...(一个不完整的类型)并且文章说不能使用 void!我打算使用std::nullptr_tint,因为我不想在库代码中添加一次性类型(但是,没关系)。
  • @CTMacUser 是的,使用intstd::nullptr_t 没有什么大问题。我只是想格外小心:)
猜你喜欢
  • 2013-08-07
  • 2012-01-22
  • 2012-10-01
  • 2020-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多