C++ SFINAE 确定函数参数的数量 (C++17)

C++ SFINAE to determine the number of function arguments (C++17)

我使用 SFINAE 创建了一个元函数来确定编译时函数的参数数量。当与函数对象一起使用时,它与 gcc 一起工作正常,但与 lambda 闭包一起使用时效果不佳,我不明白为什么。元函数在下面

template < typename T >
int val (T &&){return 0;};

template <int N, typename Functor>
struct has_args {

  template <typename F , int ... Args>
  static auto test(F, decltype(val(std::declval<F>()( Args ... ))), std::integer_sequence<int, Args ...>){
          return std::true_type{};
      };

  template <typename F, typename Val, typename Seq>
  static auto test(F, Val, Seq){
          return std::false_type{};
      };

  using type =  decltype (test(std::declval<Functor>(), 0, std::make_integer_sequence<int, N>()));
};

这是它的行为方式

struct func{
    template<typename T>
    int operator()(T){}
};
int main(){

  auto lambda0 = [](auto arg){};

  static_assert(has_arg<1, func>::type::value==true, "error");
  //static_assert(has_arg<1, decltype(lambda0)>::type::value==true, "error"); // Assertion fails!
}

完整代码(还有几个示例)在这个 git 存储库中:https://github.com/crosetto/has_args/blob/main/number_of_arguments.cpp

有人能解释为什么这不适用于 lambda 吗?

正如@rafix07 在评论中指出的那样,这里的问题是 lambda 返回 void,因此它的签名在 test 的第一个定义中不匹配,并回退到另一个重载.一种解决方法是将逗号运算符应用于 val 的参数,即更改

static auto test(F, decltype(val(std::declval<F>()( Args ... ))), std::integer_sequence<int, Args ...>)

进入

static auto test(F, decltype(val((std::declval<F>()( Args ... ),0))), std::integer_sequence<int, Args ...>)

或者,正如@Jarod42 在评论中指出的那样, val 是不必要的,可以这样写:

static auto test(F, decltype(((std::declval<F>()( Args ... ),void(),0))), std::integer_sequence<int, Args ...>)

请注意,函数体已被解析,任何使用不能用整数编译的参数都会导致编译器错误。我认为这不能以通用方式解决(尽管可以使用 int 以外的假类型,并使其满足要求 API)。