【发布时间】:2021-05-08 07:05:14
【问题描述】:
我有以下代码可以将 lambda 转换为 C 风格的函数指针。这适用于所有 lambda,包括带有捕获的 lambda。
#include <iostream>
#include <type_traits>
#include <utility>
template <typename Lambda>
struct lambda_traits : lambda_traits<decltype(&Lambda::operator())>
{};
template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<Return(Lambda::*)(Args...)> : lambda_traits<Return(Lambda::*)(Args...) const>
{};
template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<Return(Lambda::*)(Args...) const>
{
using pointer = typename std::add_pointer<Return(Args...)>::type;
static pointer to_pointer(Lambda&& lambda)
{
static Lambda static_lambda = std::forward<Lambda>(lambda);
return [](Args... args){
return static_lambda(std::forward<Args>(args)...);
};
}
};
template <typename Lambda>
inline typename lambda_traits<Lambda>::pointer to_pointer(Lambda&& lambda)
{
return lambda_traits<Lambda>::to_pointer(std::forward<Lambda>(lambda));
}
这可用于将带有捕获的 lambda 传递到 C 风格的 API:
// Function that takes a C-style function pointer as an argument
void call_function(void(*function)())
{
(*function)();
}
int main()
{
int x = 42;
// Pass the lambda to the C-style API
// This works even though the lambda captures 'x'!
call_function(to_pointer([x] {
std::cout << x << std::endl;
}));
}
鉴于此,编写一个可以将 lambdas(包括带有捕获的 lambdas)一般转换为 std::function 对象的类似模板似乎应该相对简单,但我正在努力弄清楚如何。 (我对模板元编程技术不是很熟悉,所以有点迷茫)
这是我尝试过的,但是编译失败:
template <typename Lambda>
struct lambda_traits : lambda_traits<decltype(&Lambda::operator())>
{};
template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<typename std::function<Return(Args...)>> : lambda_traits<typename std::function<Return(Args...)> const>
{};
template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<typename std::function<Return(Args...)> const>
{
using pointer = typename std::function<Return(Args...)>*;
static pointer to_pointer(Lambda&& lambda)
{
static Lambda static_lambda = std::forward<Lambda>(lambda);
return [](Args... args) {
return static_lambda(std::forward<Args>(args)...);
};
}
};
template <typename Lambda>
inline typename lambda_traits<Lambda>::pointer to_pointer(Lambda&& lambda)
{
return lambda_traits<Lambda>::to_pointer(std::forward<Lambda>(lambda));
}
这编译失败并说Lambda模板参数没有被部分特化使用。
这样做的正确方法是什么?
(注意,我一直在使用兼容 C++11 的编译器,因此无法使用 C++14 及更高版本的功能)
【问题讨论】:
-
"我有以下代码可以将 lambda 转换为 C 风格的函数指针。"非常危险。如果您使用该代码将相同类型的两个可调用对象转换为函数指针,则第二个函数指针的行为将与第一个函数指针完全相同,而忽略状态(捕获)的任何差异。这是一个坏主意。另见codereview.stackexchange.com/a/255187/49895
-
你在找C++17的
std::function(lambda)(即std::function的类模板参数推导)吗? -
是的,这看起来确实像我需要的......但我被困在使用较旧的编译器,所以我只能使用 C++11 功能。有没有办法在 C++11 中获得这种行为?
标签: c++ c++11 lambda function-pointers template-meta-programming