检查函数是否可调用

时间:2017-07-15 21:55:05

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

我正在使用如下定义的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;
};

我用它来测试声明为:

的lambda
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上编译失败? auto不应该被正确推断出来吗?

2 个答案:

答案 0 :(得分:1)

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)");

答案 1 :(得分:0)

问题不在于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类型是正确的。它看起来像我之前发布的堆栈溢出。