C++17 用非捕获 lambda 修饰 lambda 或成员函数
C++17 decorate lambda or member function with non-capturing lambda
我正在尝试制作一个函数,该函数 returns 一个装饰内部函数的非捕获 lambda(因此它可以转换为函数指针),其中内部函数可以是 lambda或成员函数指针。
Compiler Explorer link供参考,后面会剖析
我提出了两种策略,一种用于 lambda,另一种用于成员函数。具体来说,对于 lambdas
template <typename Callable>
auto decorate_lambda(Callable&& lambda) {
static const Callable callable = std::forward<Callable>(lambda);
return [](auto... args) {
return 100 + callable(std::forward<decltype(args)>(args)...);
};
}
将 lambda 保存为 static
允许 callable
用于非捕获 lambda。这很好,因为 decorate_lambda
模板实例化对于每个 lambda (IIRC) 都是唯一的。
对于成员函数,策略有些不同(注意模板参数)
template <auto callable>
auto decorate_memfn() {
return [](auto... args) {
return 100 + std::mem_fn(callable)(std::forward<decltype(args)>(args)...);
};
}
然后我可以做类似的事情
const auto decorated_lambda =
decorate_lambda([](int i) { return i + 1; });
const auto decorated_memfn =
decorate_memfn<&Worker::work>();
int (*lambda_fnptr)(int) = decorated_lambda;
int (*memfn_fnptr)(Worker&, int) = decorated_memfn;
导致可以使用的函数指针,例如在 C 接口中(最终)。
我想要的最终结果是将 decorate_lambda
和 decorate_memfn
合并为一个 decorate(lambda_or_memfn)
函数(使用模板专业化、if constexpr
或其他)。例如
decorate([](int i) { return i + 1; });
decorate(&Worker::work);
即基本上我想要 decorate_memfn(&Worker::work)
而不是 decorate_memfn<&Worker::work>()
。问题是
- 将成员函数指针作为参数而不是模板参数传递,意味着成员函数指针不再被视为
decorate_memfn
中的 static/global 变量。有没有办法强制编译器识别参数来自 static/global 并因此允许它在 lambda 中使用而不捕获?
- 用它的
static
技巧做类似于 decorate_lambda
的事情对成员函数指针不起作用,因为模板实例化不一定是唯一的(即如果两个 Callable
具有相同的签名)。不过,也许有办法让它变得独一无二?
我知道 C++20 可以提供帮助,但不幸的是我仍然坚持使用 C++17。
非常感谢任何提示!
首先,您的 decorate_lambda
有问题:如果您使用有状态可调用对象调用它,它会无声地中断。作为一个简单的检查,您可以仅在 std::is_empty_v
为真时才允许调用。
The end result that I would like is to roll decorate_lambda and decorate_memfn into a single decorate(lambda_or_memfn) function
您可以使用std::integral_constant
template<auto x>
inline constexpr std::integral_constant<decltype(x), x> constant{};
template<typename T, typename F, F T::* x>
auto decorate(std::integral_constant<F T::*, x>)
{
return [](auto&&... args) {
return 100 + std::mem_fn(x)(decltype(args)(args)...);
};
}
auto d = decorate(constant<&Worker::work>);
我正在尝试制作一个函数,该函数 returns 一个装饰内部函数的非捕获 lambda(因此它可以转换为函数指针),其中内部函数可以是 lambda或成员函数指针。
Compiler Explorer link供参考,后面会剖析
我提出了两种策略,一种用于 lambda,另一种用于成员函数。具体来说,对于 lambdas
template <typename Callable>
auto decorate_lambda(Callable&& lambda) {
static const Callable callable = std::forward<Callable>(lambda);
return [](auto... args) {
return 100 + callable(std::forward<decltype(args)>(args)...);
};
}
将 lambda 保存为 static
允许 callable
用于非捕获 lambda。这很好,因为 decorate_lambda
模板实例化对于每个 lambda (IIRC) 都是唯一的。
对于成员函数,策略有些不同(注意模板参数)
template <auto callable>
auto decorate_memfn() {
return [](auto... args) {
return 100 + std::mem_fn(callable)(std::forward<decltype(args)>(args)...);
};
}
然后我可以做类似的事情
const auto decorated_lambda =
decorate_lambda([](int i) { return i + 1; });
const auto decorated_memfn =
decorate_memfn<&Worker::work>();
int (*lambda_fnptr)(int) = decorated_lambda;
int (*memfn_fnptr)(Worker&, int) = decorated_memfn;
导致可以使用的函数指针,例如在 C 接口中(最终)。
我想要的最终结果是将 decorate_lambda
和 decorate_memfn
合并为一个 decorate(lambda_or_memfn)
函数(使用模板专业化、if constexpr
或其他)。例如
decorate([](int i) { return i + 1; });
decorate(&Worker::work);
即基本上我想要 decorate_memfn(&Worker::work)
而不是 decorate_memfn<&Worker::work>()
。问题是
- 将成员函数指针作为参数而不是模板参数传递,意味着成员函数指针不再被视为
decorate_memfn
中的 static/global 变量。有没有办法强制编译器识别参数来自 static/global 并因此允许它在 lambda 中使用而不捕获? - 用它的
static
技巧做类似于decorate_lambda
的事情对成员函数指针不起作用,因为模板实例化不一定是唯一的(即如果两个Callable
具有相同的签名)。不过,也许有办法让它变得独一无二?
我知道 C++20 可以提供帮助,但不幸的是我仍然坚持使用 C++17。
非常感谢任何提示!
首先,您的 decorate_lambda
有问题:如果您使用有状态可调用对象调用它,它会无声地中断。作为一个简单的检查,您可以仅在 std::is_empty_v
为真时才允许调用。
The end result that I would like is to roll decorate_lambda and decorate_memfn into a single decorate(lambda_or_memfn) function
您可以使用std::integral_constant
template<auto x>
inline constexpr std::integral_constant<decltype(x), x> constant{};
template<typename T, typename F, F T::* x>
auto decorate(std::integral_constant<F T::*, x>)
{
return [](auto&&... args) {
return 100 + std::mem_fn(x)(decltype(args)(args)...);
};
}
auto d = decorate(constant<&Worker::work>);