C ++ 11 lambda作为成员变量?
Posted
技术标签:
【中文标题】C ++ 11 lambda作为成员变量?【英文标题】:C++11 lambda as member variable? 【发布时间】:2011-09-29 22:10:51 【问题描述】:可以将 lambda 定义为类成员吗?
例如,是否可以使用 lambda 而不是函数对象来重写下面的代码示例?
struct Foo
std::function<void()> bar;
;
我想知道的原因是因为以下 lambda 可以作为参数传递:
template<typename Lambda>
void call_lambda(Lambda lambda) // what is the exact type here?
lambda();
int test_foo()
call_lambda([]() std::cout << "lambda calling" << std::endl; );
我想如果 lambda 可以作为函数参数传递,那么也许它们也可以存储为成员变量。
经过更多修改后,我发现这行得通(但它有点毫无意义):
auto say_hello = []() std::cout << "Hello"; ;
struct Foo
typedef decltype(say_hello) Bar;
Bar bar;
Foo() : bar(say_hello)
;
【问题讨论】:
您不知道 lambda 的类型名称,它由编译器生成(每个 lambda 函数一个)。std::function
模板正是作为这种情况下的类型橡皮擦引入的。
你试过了吗?你为什么要我们为你做这件事?如果它出错了,那么也来这里发布错误。
Lambdas 是函数对象!是的,您可以将 bar
设为 lambda。
一些 lambdas 也可以成为函数指针。
@Dani:嗯,标准组自己完成了所有工作,制定了 FDIS(国际标准最终草案)in March。剩下的就是 ISO 的一个标志。
【参考方案1】:
lambda 只是创建一个函数对象,所以,是的,您可以使用 lambda 初始化一个函数成员。这是一个例子:
#include <functional>
#include <cmath>
struct Example
Example()
lambda = [](double x) return int(std::round(x)); ;
;
std::function<int(double)> lambda;
;
【讨论】:
为什么不使用初始化列表?? 现代编译器真的没有优化这种情况吗? 我没有故意将函数放在示例中的初始化列表中,因为我认为如果您真的在真实情况下执行上述操作,您的 lambda 实际上会在初始化列表中没有意义的地方进行捕获或更复杂的事情。但当然,如果你愿意,你可以制作这个Example() : lambda([](double x) return int(std::round(x)); )
。 =)
因为 lambda 表达式产生可调用对象,所以闭包可以存储在 std::function 对象中。 Lambda 不“只是制作”函数对象。
@sungiant 好吧,如果你想争论定义,是的,lambda '只是制造'函数对象——但你是正确的,它们不'只是制造'std::function
对象。每一个 lambda——即使是那些定义完全相同的——都是一个不同类的函数对象。幸运的是,这些可以透明地和多态地与类型擦除包装器(如std::function
)一起使用。 =)【参考方案2】:
模板可以在没有类型擦除的情况下实现,但就是这样:
template<typename T>
struct foo
T t;
;
template<typename T>
foo<typename std::decay<T>::type>
make_foo(T&& t)
return std::forward<T>(t) ;
// ...
auto f = make_foo([] return 42; );
重复大家已经公开的论点:[]
不是类型,所以你不能将它用作例如像您正在尝试的模板参数。使用decltype
也是不确定的,因为 lambda 表达式的每个实例都是具有唯一类型的单独闭包对象的表示法。 (例如上面f
的类型是notfoo<decltype([] return 42; )>
。)
【讨论】:
除了你不能在不知道其类型的情况下拥有函数类型的成员变量,即。没有类型擦除。 @Alexandre 这与std::function
无关,假设您是这个意思。如果你没有,我不明白。
不一定。我只是说你不能在不知道T
的情况下声明foo<T>
类型的成员变量。为此,您必须以某种方式删除T
。这限制了你可以用你的方法做些什么(顺便说一句,这很好,我了解了std::decay
,它提醒我尽可能使用初始化列表)。
@Alexandre 这就是顶部的“无类型擦除”的含义。
就我而言,重要的是数组中的所有函数都具有相同的参数和返回类型,因此 vanilla std::function 可以正常工作。我仍然想知道开销【参考方案3】:
有点晚了,但我在这里的任何地方都没有看到这个答案。如果 lambda 没有捕获参数,则可以将其隐式转换为指向具有相同参数和返回类型的函数的指针。
例如,以下程序可以正常编译并执行您所期望的:
struct a
int (*func)(int, int);
;
int main()
a var;
var.func = [](int a, int b) return a+b; ;
当然,lambdas 的主要优点之一是捕获子句,一旦添加了它,那么这个技巧就根本不起作用。如上所述,使用 std::function 或模板。
【讨论】:
【参考方案4】:#include <functional>
struct Foo
std::function<void()> bar;
;
void hello(const std::string & name)
std::cout << "Hello " << name << "!" << std::endl;
int test_foo()
Foo f;
f.bar = std::bind(hello, "John");
// Alternatively:
f.bar = []() hello("John"); ;
f.bar();
【讨论】:
【参考方案5】:"如果 lambda 可以作为函数参数传递,那么也可以作为成员变量"
第一个是肯定的,你可以使用模板参数推导或“自动”来做到这一点。第二个可能不是,因为您需要知道声明点的类型,并且前两个技巧都不能用于此。
一个可能有效但我不知道是否有效的方法是使用 decltype。
【讨论】:
decltype
不起作用,因为 lambda 表达式的每个 出现 都是不同的类型,并且它们之间不可转换。但显然你甚至不能在 decltype 中使用 lambda。【参考方案6】:
只要 lambda 是常量(没有闭包),你就可以这样做:
#include <iostream>
template<auto function>
struct Foo
decltype(function) bar = function;
;
void call_lambda(auto&& lambda)
lambda();
int main()
Foo<[]() std::cout << "Hello"; > foo;
foo.bar();
call_lambda(foo.bar);
https://godbolt.org/z/W5K1rexv3
或者我们可以应用演绎指南使其适用于所有 lambda:
#include <iostream>
template<typename T>
struct Foo
T bar;
;
template<typename T>
Foo(T) -> Foo<std::decay_t<T>>;
void call_lambda(auto&& lambda)
lambda();
int main()
Foo foo([]() std::cout << "Hello"; );
foo.bar();
call_lambda(foo.bar);
https://godbolt.org/z/xv97Ghj7T
【讨论】:
以上是关于C ++ 11 lambda作为成员变量?的主要内容,如果未能解决你的问题,请参考以下文章