【问题标题】:c++ std::enable_if .... else?c++ std::enable_if .... 其他?
【发布时间】:2020-02-17 20:14:37
【问题描述】:
#include <stdio.h>
#include <type_traits>

void print()
{
    printf("cheers from print !\n");
}

class A 
{
  public:
  void print()
  {
      printf("cheers from A !");
  }
};


template<typename Function>
typename std::enable_if< std::is_function< 
                                typename std::remove_pointer<Function>::type >::value,
                                void >::type 
run(Function f)
{
    f();
}


template<typename T>
typename std::enable_if< !std::is_function< 
                                typename std::remove_pointer<T>::type >::value,
                                void >::type 
run(T& t)
{
    t.print();
}



int main()
{
    run(print);

    A a;
    run(a);

    return 0;
}

上面的代码按预期编译和打印:

来自印刷品的欢呼!来自 A 的欢呼!

我想表达的是:“如果模板是函数,则应用此函数,否则......”。或者换一种说法:有一个函数模板的函数版本,以及一个非函数模板的默认版本。

所以,这部分似乎有些多余,可以用“其他”条件“替换”:

template<typename T>
typename std::enable_if< !std::is_function< 
                                typename std::remove_pointer<T>::type >::value,
                                void >::type 
run(T& t)

这会存在吗?

【问题讨论】:

  • 否,但您可以使用using 语句简化表达式以减少冗长。
  • @sturcotte06 不明白你的意思 --;
  • 正如您评论并要求“过时”的 C++11:如果您限制使用较旧的 C++ 标准,您也应该使用 C++11 标记来标记您的问题。
  • @Klaus 当前的答案为 c++17 和以前的版本提供了答案,所以我想它对使用的任何版本都有用

标签: c++ typetraits enable-if


【解决方案1】:

您正在寻找的是constexpr if。这将让你编写类似的代码

template<typename Obj>
void run(Obj o)
{
    if constexpr (std::is_function_v<std::remove_pointer_t<Obj>>)
        o();
    else
        o.print();
}

Live Example

如果您无法访问 C++17 但拥有 C++14,您至少可以使用 variable template 缩短您需要编写的代码。看起来像

template<typename T>
static constexpr bool is_function_v = std::is_function< typename std::remove_pointer<T>::type >::value;

template<typename Function>
typename std::enable_if< is_function_v<Function>, void>::type 
run(Function f)
{
    f();
}


template<typename T>
typename std::enable_if< !is_function_v<T>, void>::type 
run(T& t)
{
    t.print();
}

Live Example

【讨论】:

  • @Vince 这是一个 C++17 及更高版本的解决方案。如果你需要 C++11 支持,你可以使用 using 语句来创建别名。
  • @NathanOliver 谢谢! “你只能使用 using 语句来创建别名”:不知道这是什么意思 --;
  • @Vince 添加到答案中。它实际上是一个需要的变量模板。
  • @Vince fwiw 变量模板也不是 C++11,如果你必须使用 C++11,最好使用特定的标签。从那以后发生了很多变化
  • @NathanOliver 这给了我:“错误:‘is_function_v’没有命名类型”
【解决方案2】:

如果你仅限于使用 C++11,你可以使用标签调度机制。

namespace detail
{
   template<typename Function>
   void run(std::true_type, Function& f)
   {
      f();
   }

   template<typename Object>
   void run(std::false_type, Object& o)
   {
      o.print();
   }

} // namespace detail

template<typename T>
void run(T& t)
{
   constexpr bool t_is_a_function = 
      std::is_function<typename std::remove_pointer<T>::type >::value;
   using tag = std::integral_constant<bool, t_is_a_function>;
   detail::run(tag{}, t);
}

Working example.

【讨论】:

  • 应该强调的是,虽然标记调度有点冗长 - 例如定义了更多函数 - 它更具可读性,尤其是对于不熟悉模板元编程的人。也更容易调试; SFINAE 因微妙的失败而臭名昭著。
猜你喜欢
  • 2012-10-26
  • 1970-01-01
  • 2014-08-29
  • 2013-06-03
  • 2016-02-25
  • 2014-03-27
  • 2016-12-19
  • 1970-01-01
  • 2021-03-07
相关资源
最近更新 更多