c++ - 模板函数包裹的lamda类型推导
问题描述
我实现了std::bind
如下版本的自定义替代方案:
template <typename F, typename ... Ts>
constexpr auto curry(F &&f, Ts ... args) {
return [&](auto&& ... args2) {
return f(std::forward<Ts...>(args...), args2...);
};
}
此函数获取要应用的参数的函数和包,并返回f
带有部分应用参数的 lambda args
。
另外,为了测试这段代码,我有一个函数,它获取 2 个参数并只返回第一个:
template <typename T1, typename T2>
constexpr T1 fconst(T1 &&x, T2&&) {
return x;
}
当我使用curry
如下功能时:
int main() {
auto z2 = curry(fconst, 5);
return 0;
}
我收到编译器无法推断F
类型的错误:
error: no matching function for call to ‘curry(<unresolved overloaded function type>, int)’
17 | auto z2 = curry(fconst, 5);
candidate: ‘template<class F, class ... Ts> constexpr auto curry(F&&, Ts ...)’
15 | constexpr auto curry(F &&f, Ts ... args) {
| ^~~~~
note: template argument deduction/substitution failed:
note: couldn’t deduce template parameter ‘F’
17 | auto z2 = curry(fconst, 5);
但是当我curry
将函数实现为宏而不是模板函数时:
#define curry(f, args...) \
[&](auto&& ... args2) { \
return f(args, args2...); \
};
我的主要代码编译成功。
我使用启用了 g++11 和 c++20。
我的问题是:
- 为什么编译器不能推断
F
模板的类型,但可以在传递 lambda 时做到这一点? - 我的
curry
函数可以使用模板函数来实现,还是宏是唯一可行的方法?
解决方案
首先,你curry
是危险的,因为里面的 lambda 通过引用捕获本地参数。一旦curry
返回,您就会有悬空的裁判。
正确的版本是:
template <typename F, typename ... Ts>
constexpr auto curry(F &&f, Ts ... args) {
return [...args=std::move(args), f=std::forward<F>(f)](auto&& ... args2) mutable {
return std::invoke(f,args..., std::forward<decltype(args2)>(args2)...);
};
}
f
,args
处理 显示了两种相同的样式。- 在调用方复制 -
args
。 - 或在捕获时复制/移动(向前)。
- 在调用方复制 -
- (参数包捕获是 C++20)。使用元组和 有一个更丑陋的解决方法
std::apply
,询问您是否需要它。 std::bind
从不移动其捕获的参数,它们总是作为左值传递给被调用者。这使得重复调用安全。args2
应该正确转发。
不幸的是,没有无宏的解决方案。这是重载函数集的固有问题。不能传递这样的集合,C++ 根本不支持。有一些论文试图解决这个问题 - 我知道P1170R0。但到目前为止没有一个被接受。
解决方法本质上是您想出的以及为什么您的第二个示例有效 - 宏能够粘贴函数名称,无论它是否重载(或者它实际上是什么)。
#define overload_set(overloaded_f) \
[](auto&& ... args) { \
return overloaded_f(std::forward<decltype(args)>(args)...);} \
这是就地完美转发 lambda,overloaded_f
内部带有复制粘贴符号。尽管如此,一个人不能像任何其他普通函数一样获取 this 的地址(或者更确切地说不应该),但它可以传递给 eg curry
。
完整示例
#include <iostream>
template <typename F, typename... Ts>
constexpr auto curry(F&& f, Ts... args) {
return [... args = std::move(args),
f = std::forward<F>(f)](auto&&... args2) mutable {
return f(args..., std::forward<decltype(args2)>(args2)...);
};
}
template <typename T1, typename T2>
constexpr T1 fconst(T1&& x, T2&&y) {
std::cout<<"Value x:" << x<<'\n';
std::cout<<"Value y:" << y<<'\n';
return x;
}
#define overload_set(overloaded_f) \
[&](auto&&... args) { \
return overloaded_f(std::forward<decltype(args)>(args)...); \
}
struct Foo{
Foo()=default;
Foo(Foo&&)=default;
Foo(const Foo&)=delete;
operator int(){ return 42;}
};
int main() {
// Overloaded function can now be stored in a lambda.
auto stored_set= overload_set(fconst);
// And called as ordinary function.
auto ret =stored_set(1.0, 2.0);
std::cout<<"Returned value: " <<ret <<'\n';
// Overloaded set now can be passed around.
// But it is a functor, not a function.
auto curried_fnc= curry(overload_set(fconst),1.0);
// Curry works
auto ret2 = curried_fnc(2.0);
std::cout<<"Returned value: " <<ret2<<'\n';
// Move-only values work too.
std::cout<<"Curry move\n";
curried_fnc(Foo{});
}
输出
Value x:1
Value y:2
Returned value: 1
Value x:1
Value y:2
Returned value: 1
Curry move
Value x:1
Value y:42
推荐阅读
- c++ - std::shared_ptr(s) 和内存泄漏
- ios - 如何在不改变所有设备的情况下改变不同设备的特征。在 Xcode 中使用堆栈视图
- vue.js - 部署 Vue Web 应用程序总是会导致客户端出现缓存问题
- c# - “找不到方法:'System.Net.Http.HttpRequestMessage System.Web.Http.Controllers.HttpActionContext.get_Request()'
- javascript - 如何在我的 html 中以表格形式打印从服务器到客户端的变量?
- unity3d - 按住键(c# unity)
- javascript - 如何通过表单上传图片?
- javascript - 当我在终端上输入“heroku logs --tail”时如何修复以下错误?这是什么意思?
- python - 如何从字典中打印具有可变数量的键+值的格式化字符串
- python - 无法激活环境