为什么我要避免std::Enable_if在函数签名中

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (143)

std::enable_if可用作函数参数、返回类型或类模板或函数模板参数,以便有条件地从重载解析中删除函数或类。

作为函数参数:

template<typename T>
struct Check1
{
   template<typename U = T>
   U read(typename std::enable_if<
          std::is_same<U, int>::value >::type* = 0) { return 42; }

   template<typename U = T>
   U read(typename std::enable_if<
          std::is_same<U, double>::value >::type* = 0) { return 3.14; }   
};

作为模板参数:

template<typename T>
struct Check2
{
   template<typename U = T, typename std::enable_if<
            std::is_same<U, int>::value, int>::type = 0>
   U read() { return 42; }

   template<typename U = T, typename std::enable_if<
            std::is_same<U, double>::value, int>::type = 0>
   U read() { return 3.14; }   
};

作为返回类型:

template<typename T>
struct Check3
{
   template<typename U = T>
   typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
      return 42;
   }

   template<typename U = T>
   typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
      return 3.14;
   }   
};
  • 应该选择哪种解决方案,为什么我要回避其他人?
  • 在这种情况下“避免性病::启用”_如果在函数签名中“将使用作为返回类型(它不是普通函数签名的一部分,而是模板专门化的一部分)?”
  • 成员函数模板和非成员函数模板有什么不同吗?
提问于
用户回答回答于

enable_if在模板参数方法中,至少有两个优点:

  • 可读性*启用_如果Use和返回/参数类型没有合并到一个混乱的TypeName消歧器和嵌套类型访问中;即使使用别名模板可以减轻消歧器和嵌套类型的杂乱,这仍然会将两个不相关的东西合并在一起。启用_如果Use与模板参数相关,则与返回类型无关。在模板参数中包含它们意味着它们更接近重要的内容;
  • 普遍适用性:构造函数没有返回类型,一些运算符不能有额外的参数,因此其他两个选项都不能在任何地方应用。放能_如果在模板中,参数在任何地方都可以工作,因为您只能在模板上使用SFINAE。

用户回答回答于

  1. 如果您在enable_if包含嵌套模板或类型定义(提示:查找::,则这些嵌套的温度或类型的解析通常是非推断语境...。在这样一个非推导的上下文中,任何替换失败都是一个误差...
  2. 多重的各种条件enable_if重载不能有任何重叠,因为重载解析将是不明确的。这是您作为一个作者需要检查自己的东西,尽管您会得到良好的编译器警告。
  3. enable_if在过载解析期间操纵一组可行的函数,根据其他作用域(例如,通过ADL)引入的其他函数的存在,这些函数可能具有令人惊讶的交互作用。这使得它不太强大。

一个很好的选择是使用标签调度,即委托给实现功能(通常在detail命名空间或帮助器类中的参数),该类基于与使用的编译时条件相同的虚拟参数。enable_if...

template<typename T>
T fun(T arg) 
{ 
    return detail::fun(arg, typename some_template_trait<T>::type() ); 
}

namespace detail {
    template<typename T>
    fun(T arg, std::false_type /* dummy */) { }

    template<typename T>
    fun(T arg, std::true_type /* dummy */) {}
}

标记分配不操作重载集,而是通过编译时表达式提供适当的参数来帮助准确地选择所需的函数。在我的经验中,这更容易调试和纠正。如果是一个具有复杂类型特性的有抱负的库作者,可能需要enable_if但是,对于大多数编译时条件的常规使用,不建议使用这种方法。

扫码关注云+社区