我使用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
有没有人解释为什么这不能与lambdas一起工作?
正如@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)。