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&lt;decltype([] return 42; )&gt;。)

【讨论】:

除了你不能在不知道其类型的情况下拥有函数类型的成员变量,即。没有类型擦除。 @Alexandre 这与std::function 无关,假设您是这个意思。如果你没有,我不明白。 不一定。我只是说你不能在不知道T 的情况下声明foo&lt;T&gt; 类型的成员变量。为此,您必须以某种方式删除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作为成员变量?的主要内容,如果未能解决你的问题,请参考以下文章

在向量成员变量中存储线程触发断点

如何使用lambda表达式捕获局部变量?

在成员函数内的 lambda 捕获列表中使用成员变量

编译失败,将静态变量作为私有成员变量的c ++程序[重复]

C ++ lambda函数作为类成员:奇怪的行为

5月11日黑马java之类作为成员变量类型解析