【问题标题】:C++11 lambda as member variable?C ++ 11 lambda作为成员变量?
【发布时间】: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 的一个标志。

标签: c++ c++11 lambda


【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-08
        • 1970-01-01
        • 2018-12-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多