参数列表中带有省略号的 C++ Lambda
C++ Lambdas with Ellipses in the Parameter List
我正在开发一个使用 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" 只命名其中的一个、两个还是 none):
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 表达式吗?
However, I'm not aware of any (simple) way (in C++14) to deduce the number of parameters to an arbitrary lambda object.
在我看来,您正在寻找 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 风格的 variadic functions。使用它们没有什么错误,如果您不想访问这些值(这有点难看),那很好。
然而, 看起来你真正想要解决的潜在问题是让你的库找到参数的数量(或 arity) 的 function/lambda/...,您可以使用模板元编程来完成 - 您的用户无需解决该问题。
披露:在我也在研究的库中有一个实现,here。
这是一个简单的例子:
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
一起正常工作,否则您将 运行 出现 .
之类的错误
不幸的是,这将不能处理重载或模板化的operator()
(例如,如果您在 lambda 中使用 auto
类型的参数)。如果您还想支持函数而不是 lambda,则可能需要额外的专业化。
我正在开发一个使用 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" 只命名其中的一个、两个还是 none):
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 表达式吗?
However, I'm not aware of any (simple) way (in C++14) to deduce the number of parameters to an arbitrary lambda object.
在我看来,您正在寻找 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 风格的 variadic functions。使用它们没有什么错误,如果您不想访问这些值(这有点难看),那很好。
然而, 看起来你真正想要解决的潜在问题是让你的库找到参数的数量(或 arity) 的 function/lambda/...,您可以使用模板元编程来完成 - 您的用户无需解决该问题。
披露:在我也在研究的库中有一个实现,here。
这是一个简单的例子:
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
一起正常工作,否则您将 运行 出现
不幸的是,这将不能处理重载或模板化的operator()
(例如,如果您在 lambda 中使用 auto
类型的参数)。如果您还想支持函数而不是 lambda,则可能需要额外的专业化。