扯 C++ 里的 Lambda
之前写(抄) parsec 的时候, 在重载 operator>> 的时候, operator>> 需要接收一个 lambda, 之后返回一个 Component<R>, 其中 R 是接收 lambda 的返回值类型, 所以就要搞到 lambda 对应的函数类型
在一开始我是直接用 std::function 做的, 但是众所周知, 下面这样的写法是匹配不了的:
template<typename R, typename ...Args>
ParsecComponent<R> operator>>(std::function<R(Args...)> callback) {
ParsecComponent<R> component;
...
return component;
}
因为 lambda 表达式到 std::function 要进行类型转换, 毕竟是两个类型, 所以指明 std::function 的模板实参的时候才能进行 lambda -> std::function 的隐式转换, 不过一开始为了偷懒, 而且我只需要拿到 lambda 的返回值类型, 就这样写了:
template<typename Func>
auto operator>>(Func &&callback) {
using NewResult = typename decltype(std::function(callback))::result_type;
ParsecComponent<NewResult> component;
...
return component;
}
所以说 auto 这种东西还真是好用啊, 类型还可以拖延到函数体里做(
这样做总归有种脱裤子放屁的感觉, 那么怎么不通过 std::function 就能拿到 lambda 表达式对应的函数类型呢?
众所周知, 每一个 lambda 表达式的类型都是不一样的, 比如:
template<typename Func>
void print(Func &&f) {
std::cout << typeid(Func).name() << std::endl;
}
int main() {
print([](){});
print([](){});
return 0;
}
我这里输出的结果是
Z4mainE3$_0
Z4mainE3$_1
毕竟如果要每个定义相同的 lambda 的类型相同是不现实而而且没有必要的, 何况还有闭包捕获之类的复杂性需要考虑, 但是众所周知, lambda 返回的是一个对象, 也即它的类型重载了 operator(), 而 operator() 又是一个函数, 那么我们不久可以通过推导其重载的 operator() 函数类型拿到 lambda 对应的函数类型了吗(
所以这件事情很清晰明了了, 我们只需要拿到 lambda 表达式产生的匿名类型, 然后根据类成员函数寻址到它的 operator(), 然后推导出 operator() 的函数签名就可以了!
那就划分成三步吧
一, 得到 lambda 对应的匿名类类型
这一步是很简单的, 因为只要模板实参自动推导一下就出来了
template<typename Func>
void foo(Func &&lambda);
那现在这个 Func 就是我们需要的类型
二, 推导 Func 重载的 operator() 函数签名
因为当前我们只有一个 Func 类型, 但是如果要获取 operator() 的签名的话, 我们还需要对 operator() 的类型做一个特化, 比如
template<typename T> struct lambda_traits;
template<typename R, typename ClassType, typename ...Args>
struct lambda_traits<R(ClassType::*)(Args...) const> {
using result_type = R;
using args_type = std::tuple<Args...>;
template<size_t index>
using arg_type_at = std::tuple_element_t<index, args_type>;
static constexpr size_t arity = sizeof...(Args);
};
很简单对吧, 只需要对类的成员函数的类型做一个特化就好了
其实这个时候已经可以用了, 比如
template<typename Func,
typename R = typename lambda_traits<decltype(&Func::operator())>::result_type>
void foo(Func &&lambda);
三, 包装一下
其实可以看到第二步的时候已经可以用了, 那么我们只需要把调用的过程包装一下, 但是由于我们拿到的是一个 Func, 所以需要把之前的 lambda_traits 变成基类, 然后另 Func 实例化的模板类继承它就可以了
template<typename T> struct lambda_traits_base;
template<typename R, typename ClassType, typename ...Args>
struct lambda_traits_base<R(ClassType::*)(Args...) const> {
using result_type = R;
using args_type = std::tuple<Args...>;
template<size_t index>
using arg_type_at = std::tuple_element_t<index, args_type>;
static constexpr size_t arity = sizeof...(Args);
};
template<typename Func>
struct lambda_traits : lambda_traits_base<decltype(&Func::operator())> {};
支持 C++17 的 lambda 现在是没有问题了, 那么有人要问了, C++20 的 lambda 是支持模板 operator() 的, 这个显然是不支持的啊, 是垃圾
那就来接轨一哈 20 吧, 反正接轨了之后是不影响 17 的 lambda 推导的
支持 template operator() 的 lambda 顾名思义就是长这样子的啦:
auto foo = []<typename T, typename ...Args>(Args...) -> T { return T(); };
foo.operator()<...>(...);
可以看出来(目前)如果要调用这个 lambda 的话, 我们需要显式地指明模板的实参, 所以在推导的时候, 模板实参的信息也是要提供的, 那么只需要简单地修改一下我们的 lambda_traits:
template<typename Func, typename ...Args>
struct lambda_traits : lambda_traits_base<decltype(&Func::template operator()<Args...>)> {};
template<typename Func>
struct lambda_traits : lambda_traits_base<decltype(&Func::operator())> {};
这样一来就可以了, 不过还有一种特殊情况, 比如说我们有一个这样类似提供了 template operator() 的类:
struct Foo
{
template<typename ...Args>
void operator() {};
};
当我们在获取他无模板实参的 operator() 时, 我们只能通过 &Foo::template operator()<> 而不能写 &Foo::operator(), 当然这也是显而易见的, 不过如果我们的 lambda 支持的 template operator() 能够接收无实参的实例化的话, 就会导致前边的 lambda_traits 失效, 所以我们需要在模板实参 Args 为空的时候判断一下要取 operator() 还是 template operator()<>
template<typename T, typename = std::void_t<>>
struct call_which {
using type = decltype(&T::operator());
};
template<typename T>
struct call_which<T, std::void_t<decltype(&T::template operator()<>)>> {
using type = decltype(&T::template operator()<>);
};
template<typename T>
using call_which_t = typename call_which<T>::type;
template<typename T, typename ...Args>
struct lambda_traits : lambda<decltype(&T::template operator()<Args...>)> {};
template<typename T>
struct lambda_traits<T> : lambda<call_which_t<T>> {};
通过 void_t 判断了一下是不是具有 template operator()<> 就可以了
扯 C++ 里的 Lambda的更多相关文章
- C++11里面的Lambda表达式
Lambda Expressions in C++ C++中的Lambda表达式 In Visual C++, a lambda expression—referred to as a lambda— ...
- 如何设计一门语言(七)——闭包、lambda和interface
人们都很喜欢讨论闭包这个概念.其实这个概念对于写代码来讲一点用都没有,写代码只需要掌握好lambda表达式和class+interface的语义就行了.基本上只有在写编译器和虚拟机的时候才需要管什么是 ...
- c++11 新特性之lambda表达式
写过c#之后,觉得c#里的lambda表达式和delegate配合使用,这样的机制用起来非常爽.c++11也有了lambda表达式,形式上有细小的差异.形式如下: c#:(input paramete ...
- Python 与 C# lambda表达式比较
Python里到lambda表达式非常简约, lam =lambda a: a*2 --> lam(3) 6 在某些情况下确实挺好用到.但是相比C#到lambda表达式,还是不够强大(我不是在黑 ...
- 30 分钟 Java Lambda 入门教程
Lambda简介 Lambda作为函数式编程中的基础部分,在其他编程语言(例如:Scala)中早就广为使用,但在Java领域中发展较慢,直到java8,才开始支持Lambda. 抛开数学定义不看,直接 ...
- 在Lambda表达式中使用循环变量
在C#5.0之前,如果在foreach循环中的lambda表达式里使用循环变量,那么你会发现一些意想不到的现象,例子如下: , , , }; var actions = new List<Act ...
- 匿名方法和Lambda表达式
匿名方法本质上是一传递给委托的代码块,是使用委托的另一种方法. 规则: 1.匿名方法中不能使用跳转语句跳至次匿名方法的外部,反之亦然:匿名方法外部的跳转语句也不能跳转到匿名方法的内部: 2.在匿名方法 ...
- 什么是C# Lambda表达式?形如:p=>p.abc
这里介绍C# Lambda表达式,它实际上和匿名方法没有什么不同.Lambda的输入参数就对应着delegate括号里面的参数,由于C# Lambda表达式可以推断参数的类型,所以这里的参数无需声明. ...
- 闭包、lambda和interface
闭包.lambda和interface 人们都很喜欢讨论闭包这个概念.其实这个概念对于写代码来讲一点用都没有,写代码只需要掌握好lambda表达式和class+interface的语义就行了.基本上只 ...
随机推荐
- win10 防火墙配置 允许局域网其他设备访问本地工程
1.进入防护墙并找到高级设置 2.选择属性 3.将入站连接设为允许,有三个别忘了,[域配置文件,专用配置文件,公用配置文件]
- nvm安装vue-cli
使用nvm可以更换nodejs版本.方便不同项目的切换 1.安装nvm(本人提供版本为1.1.9,当前最新) ① 到官网自行下载 https://github.com/coreybutler/nvm- ...
- STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- 开源数据可视化BI工具SuperSet(安装)
本次安装教程共分两大步骤,因为Superset 基于python3编写的web应用(flask) 所以要求python3环境,故首先要将linux系统自带的环境进行升级,已经是python3的可跳过- ...
- 【失败经验分享】android下使用支持opencl的cv::dft()
1.使用了UMat,但是并未使用GPU计算 cv::dft()函数的定义是: void cv::dft( InputArray _src0, OutputArray _dst, int flags, ...
- 【记录一个问题】linux下使用opencv中的UMat,性能并未提升,反而略有下降
使用后性能略微下降,一开始怀疑是UMat拷贝的问题.运行 nvidia-smi -l 1, 发现GPU占用始终为0.说明opencl使用的是CPU版本,而不是GPU版本.明天试验opencl的GPU版 ...
- word缺失字体问题
问题 有时候word会确实一些公文使用的字体,所以会用微软雅黑进行自动替换.所以你看到和打印的仿宋等字体,会被替换成微软雅黑,这对于政府公文或者企业行政文档是不好的. 这里面的字体替换你可以用一些字体 ...
- 浅谈kali : aircrack-ng套件
aircrack-ng Aircrack-ng是一个与802.11标准的无线网络分析有关的安全软件,主要功能有:网络侦测,数据包嗅探,WEP和WPA/WPA2-PSK破解.Aircrack-ng可以工 ...
- python使用pip安装库超时报错解决办法
原因:pip源超时了,安装不上 pip install matplotlib -i http://pypi.douban.com/simple --trusted-host pypi.douban.c ...
- 右键没有word?excel?ppt?注解表该改改啦
✿[office 2019]office2010版本以上的都可以(例如:office 2010.office 2016.office 2019) 一.快速方法解决右键没有word: 在电脑桌面右键一个 ...