问题描述
这是最小的可重现代码,
#include <iostream>
#include <string>
void bar(std::string s,int x){
std::cout <<std::endl<< __FUNCTION__<<":"<<s<<" "<<x;
}
using f_ptr = void(*)(std::string);
void foo(f_ptr ptr){
ptr("Hello world");
}
template<typename T> void fun(T f){
static int x;
std::cout <<std::endl<<x++<<std::endl;
f("Hello World");
}
int main()
{
//case:1
f_ptr ptr1 = [](std::string s){bar(s,10);};
// foo(ptr1);
//case:2
static int x =10;
f_ptr ptr2 = [x](std::string s){bar(s,x);};
//foo(ptr2);
//case:3
int y =10;
f_ptr ptr3 = [y](std::string s){bar(s,y);}; /* error*/
foo(ptr3);
//case:4
int z = 12;
fun([z](std::string s){bar(s,z);});
return 0;
}
错误:
main.cpp:25:50: error: cannot convert ‘main()::’ to ‘f_ptr {aka void (*)(std::basic_string)}’ in initialization
f_ptr ptr3 = [y](std::string s){bar(s,y);}; /* error*/
我的问题是,
- 有没有办法通过 lambda 转发额外的参数,比如
case:3
? - 是什么转换导致了
case:3
中的错误? - 在
case:4
中,typename T
推导出什么?
解决方法
- 有没有办法通过 lambda 转发额外的参数,比如
case:3
? - 是什么转换导致了
case:3
中的错误?
带有捕获列表的 Lambda 无法隐式转换为函数指针;没有捕获的 lambdas 可以。您可以改用 std::function
,
void foo(std::function<void(std::string)> f){
f("Hello world");
}
或者像 fun
那样直接使用 lambda。
- 在
case:4
中,typename T
推导出什么?
类型将是唯一的闭包类型; lambda 表达式是该类型的纯右值表达式。
,一些细节,为什么这会编译而案例 3 不会:
//case:2
static int x =10;
f_ptr ptr2 = [x](std::string s){bar(s,x);};
它可以编译,因为它实际上不捕获任何内容,因此可以将 lambda 绑定到函数指针,这在有效捕获情况下是不允许的。标准说:
5.1.1/2 lambda 捕获中的名称应在 lambda 表达式上下文的范围内,并且应是 this 或引用本地 具有自动存储期限的变量或引用。
所以至少没有指定静态变量捕获情况的行为,但不一定是未定义的行为(实现的自由度)。
请注意,捕获静态变量可能会导致令人怀疑的可怕问题,因为从捕获列表的语义中可能期望复制值,但实际上没有复制任何值!
还要注意,这个问题对于全局变量也是一样的(没有自动存储持续时间)!