【问题标题】:Check if a function is callable检查函数是否可调用
【发布时间】:2017-12-20 17:47:14
【问题描述】:

我使用的is_callable结构定义如下

template <typename F, typename... Args>
struct is_callable {
  template <typename U>
  static auto test(U* p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type());

  template <typename U>
  static auto test(...) -> decltype(std::false_type());

  static constexpr bool value = decltype(test<F>(nullptr))::value;
};

我正在使用它来测试声明为:

template <typename T>
struct runner {
  T t;

  template <typename F, typename = typename std::enable_if<is_callable<F, T&>::value || is_callable<F, T&&>::value>::type>
  void run(F&& f) { 
    return f(t); 
  }
};

runner<int> a{0};
a.run([&] (auto& x) {
  x++;
});

为什么在 AppleClang 上的 enable_if 上编译失败? autos 不应该被正确推断吗?

【问题讨论】:

  • Works for me。你的clang 版本是什么,具体的错误信息是什么?
  • @Rakete1111 candidate template ignored: disabled by 'enable_if' Apple LLVM 版本 8.1.0 (clang-802.0.42)
  • 你为什么要检查它是否可以用T&amp; T&amp;&amp; 调用?你用T&amp; 调用它,检查一下。
  • 为什么要在 false_type() 周围加上 decltype?您的函数只能返回 false_type。另外,为什么其他测试中的 void() ?您可能需要 F* 类型的 nullptr。最后,您的 enable_if 用户是错误的。由于泛型 lambda 需要 C++14,不妨使用更简单的 enable_if_t。

标签: c++ lambda c++14 metaprogramming enable-if


【解决方案1】:

问题不在于is_callable 测试类,而在于你对它的使用。

当在函数的模板参数列表中使用std::enable_if时,你必须这样使用它:

template <typename T>
struct runner {
  T t;

  template 
  <
  typename F, 
  std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value>* = nullptr
  >
  void run(F&& f) { 
    return f(t); 
  }
};

您尝试使用的表单用作尾随返回类型:

  template<typename F>
  auto run(F&& f) 
  -> std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value>
  { 
    return f(t); 
  }

is_callable 类型是正确的。它看起来像我之前发布到堆栈溢出的一个。

【讨论】:

  • 你为什么认为你必须那样使用它?
  • @Barry 因为它是启用函数的 3 种正确方法之一:type* = nullptr、尾随类型或 false 参数。
  • 我不知道你为什么这么认为。 template &lt;..., typename = std::enable_if_t&lt;...&gt;&gt; 相当常用。
  • @Barry 启用类,而不是函数。
  • 默认模板参数不起作用的唯一情况是您需要将 SFINAE 应用于多个其他相同的重载,因为它不是签名的一部分。这不适用于 OP 的代码。
【解决方案2】:

true_type test 的情况看起来不对。无论如何,您的代码比它需要的要复杂得多。尝试以下最小的工作示例:

#include <utility>

template<typename F, typename...Args> struct is_callable {
  template<typename F2, typename...Args2> static constexpr std::true_type
  test(decltype(std::declval<F2>()(std::declval<Args2>()...)) *) { return {}; }

  template<typename F2, typename...Args2> static constexpr std::false_type
  test(...) { return {}; }

  static constexpr bool value = decltype(test<F, Args...>(nullptr))::value;
};

void f0();
static_assert(is_callable<decltype(f0)>::value, "f0()");
static_assert(!is_callable<decltype(f0), int>::value, "f0(0)");

int f1(int);
static_assert(!is_callable<decltype(f1)>::value, "f1()");
static_assert(is_callable<decltype(f1), int>::value, "f1(0)");
static_assert(!is_callable<decltype(f1), int, int>::value, "f1(0, 0)");

auto __attribute__((unused)) f2 = [](int, char *) { return 7; };
static_assert(is_callable<decltype(f2), int, char *>::value, "f2(int, char *)");
static_assert(!is_callable<decltype(f2), int, int>::value, "f2(int, int)");

【讨论】:

  • 那是一个可怕的 is_callable.
  • 有什么可怕的?它适用于 C++ 11、14 和 17,并且非常明确和清晰地使用 SFINAE。
猜你喜欢
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 2021-12-15
  • 2011-09-08
相关资源
最近更新 更多