我正在开发一个使用 lambda 来描述表达式术语范围的库。 由于库必须分发唯一的整数来标识每个变量,因此最好由库(而不是用户(构造变量,并且用户代码将它们作为 lambda 参数接收。
(换句话说,我正在实现来自miniKanren的"call\fresh"的C++模拟。
由于用户可能希望在特定范围内引入从零到许多新变量的任何数字,我希望用户能够将具有不同参数数量的 lambda 传递给库。 但是,我不知道有任何(简单(方法(在 C++14 中(来推断任意 lambda 对象的参数数量。
我想到了一个想法,为什么不将固定数量(例如 10(的变量 id 参数传递给 lambda,并让用户代码在 lambda 中使用省略号来忽略不需要的省略号? 像这样:
auto no_args = call_fresh([](...) { return success(); });
auto one_arg = call_fresh([](var A, ...) { return A == 1; });
auto two_args = call_fresh([](var A, var B, ...) { return A == 1 && B == 2; });
编译器资源管理器似乎接受 lambda 参数列表中的省略号,至少在 gcc 中是这样。
它被称为这样的东西(请注意代码如何总是传递 10 个变量 id,无论"f"只命名一个、两个还是不命名(:
template <typename F>
auto call_fresh(F f)
{
return [f](StateCounter sc) {
return f(sc+0,sc+1,sc+2,sc+3,sc+4,
sc+5,sc+6,sc+7,sc+8,sc+9);
};
}
当然,我很惊讶这是一个功能,有什么理由不使用带有省略号的 lambda?
但是,我不知道有任何(简单的(方法(在 C++14 中(可以推断任意 lambda 对象的参数数量。
在我看来,您正在寻找sizeof...()
而不是多变参数auto
参数列表
#include <iostream>
int main ()
{
auto l = [](auto ... as) { return sizeof...(as); };
std::cout << l(1, 2L, 3.0, 4.0f, "5") << std::endl; // print 5
}
你的 lambda 本质上是 C 风格的可变参数函数。使用它们没有错,如果你不想访问这些值(这有点丑陋(,那很好。
但是,您似乎真正想要解决的潜在问题是让您的库找到函数/lambda/...的参数数量(或arity(,您可以使用模板元编程来完成 - 无需用户解决此问题。
披露:在我也在研究的库中有一个实现,在这里。
下面是一个简单的示例:
template <typename Callable>
struct function_arity : public function_arity<decltype(&Callable::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_arity<ReturnType(ClassType::*)(Args...) const>
{
constexpr static size_t arity = sizeof...(Args);
};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_arity<ReturnType(ClassType::*)(Args...)>
{
constexpr static size_t arity = sizeof...(Args);
};
编译器将自动为您推断参数类型,sizeof...
将为您提供所需的参数数量。
然后,您可以使用function_arity<decltype(lambda)>::arity
来获取 lambda 的参数数。最后一个版本处理mutable
lambda,其中调用运算符是非恒定的。您可能还希望扩展它以与noexcept
正常工作,否则您会遇到像这个libc ++错误这样的错误。
不幸的是,这不适用于重载或模板化operator()
(例如,如果您在 lambda 中使用auto
-type 参数(。如果您还想支持函数而不是 lambda,则可能需要其他专用化。