【问题标题】:How can I store a lambda expression as a field of a class in C++11?如何在 C++11 中将 lambda 表达式存储为类的字段?
【发布时间】:2012-02-29 11:40:09
【问题描述】:

我想创建一个类,客户端可以在其中存储像 []() -> void {} 这样的 lambda 表达式作为类的字段,但我不知道该怎么做。 One answer suggested using decltype,我试过但没有成功。这是ideone source link。以下是出处和结果:

#include <cstdio>
auto voidLambda = []()->void{};

class MyClass {
public:
     decltype(voidLambda) t;
     MyClass(decltype(voidLambda) t) { 
        this->t = t;
     }
};

int main() {
   MyClass([] {
      printf("hi");
   });
}

结果:

prog.cpp: In constructor 'MyClass::MyClass(<lambda()>)':
prog.cpp:3:79: error: no matching function for call to '<lambda()>::__lambda0()'
prog.cpp:2:20: note: candidates are: <lambda()>::<lambda>(const<lambda()>&)
prog.cpp:2:20: note:                 <lambda()>::<lambda>(<lambda()>&&)
prog.cpp:3:88: error: no match for 'operator=' in '((MyClass*)this)->MyClass::t = t'
prog.cpp: In function 'int main()':
prog.cpp:5:27: error: no matching function for call to 'MyClass::MyClass(main()::<lambda()>)'
prog.cpp:3:48: note: candidates are: MyClass::MyClass(<lambda()>)
prog.cpp:3:14: note:                 MyClass::MyClass(const MyClass&)

有人知道怎么做吗?

【问题讨论】:

  • 每个 lambda 表达式都会创建自己独特的类型。在auto A = [](){}; auto B = [](){}; AB 不是同一个类型。
  • 很遗憾,struct A { auto x = 0; }; 是不允许的。

标签: c++ class lambda c++11 field


【解决方案1】:

如果您希望类成员成为 lambda 表达式,请考虑使用 std::function&lt;&gt; 包装器类型(来自 &lt;functional&gt; 标头),它可以容纳任何可调用函数。例如:

std::function<int()> myFunction = []() { return 0; }
myFunction(); // Returns 0;

这样,您不需要知道 lambda 表达式的类型。您可以只存储适当函数类型的std::function&lt;&gt;,模板系统将为您处理所有类型。更一般地,任何具有适当签名的可调用实体都可以分配给 std::function&lt;&gt;,即使该函子的实际类型是匿名的(在 lambda 的情况下)或非常复杂。

std::function 模板中的类型应该是与您要存储的函数相对应的函数类型。因此,例如,要存储一个接收两个 ints 并返回 void 的函数,您将创建一个 std::function&lt;void (int, int)&gt;。对于不带参数并返回int 的函数,您将使用std::function&lt;int()&gt;。在您的情况下,由于您想要一个不带参数并返回 void 的函数,因此您需要这样的东西:

class MyClass { 
public:
    std::function<void()> function;
    MyClass(std::function<void()> f) : function(f) {
        // Handled in initializer list
    }
};

int main() {
    MyClass([] {
        printf("hi")
    }) mc; // Should be just fine.
}

希望这会有所帮助!

【讨论】:

  • 问题:-&gt; void 真的有必要吗?我知道 return 子句可以在许多情况下被省略(并因此推断),这让我觉得这是一个简单的案例(没有return)。
  • @MatthieuM.- 实际上我不确定!我的印象是你必须拥有它,但如果我弄错了,我很乐意更新这个答案。
  • @MatthieuM.:如果只有一个语句是return,则将推断返回类型。否则,返回类型始终为void。因此,-&gt; void 可以省略。
  • 顺便说一句,你甚至可以省略括号(),因为没有参数。
  • 为什么人们不在他们的答案中包含相关的内容?这将节省我这么多的谷歌搜索!
【解决方案2】:

我能想到的在类中存储 lambda 的唯一方法是使用带有帮助器 make_ 函数的模板:

#include <cstdio>
#include <utility>

template<class Lambda>
class MyClass {
    Lambda _t;
public:
    MyClass(Lambda &&t) : _t(std::forward<Lambda>(t)) {
        _t();
    }
};

template<class Lambda>
MyClass<Lambda> make_myclass(Lambda &&t) {
    return { std::forward<Lambda>(t) };
}

int main() {
    make_myclass([] {
        printf("hi");
    });
}

【讨论】:

  • 通过引入这个帮助器 make_myclass 来避免指定模板参数的需要。那是Object generator idiom
  • 使用 C++17 CTAD make_myclass 不再需要。
  • 我正在尝试在我的程序中实现这种类型的东西。我该如何删除辅助函数?
  • @James51332 只是MyClass my ( []{ printf("hi"); } );
  • 谢谢。哈哈。很久以前我想出了一个更理想的实现(类型擦除),但是谢谢。
【解决方案3】:

[](空捕获)的情况下,可以使用简单的函数指针。声明语法是 ReturnType (*pointer_name) (Arg1T, Arg2T); 用于指针,ReturnType (&amp;ref_name) (/*void*/); 用于引用(不能为空)。具有空捕获块的 Lambda 可隐式转换为具有相同签名的函数指针。并且std::function 具有运行时和大小(至少大三倍)开销。

struct S
{
  void (*f_p)() {}; // `{}` means `= nullptr`;
};

int main()
{
    S s { [] { std::cout << "Lambda called\n"; }};

    s.f_p();

    S s2;
    if (s2.f_p) // check for null
        s.f_p();

    s2.f_p = [] { std::cout << "Lambda2 called\n"; };
    s2.f_p();

    s2.f_p = std::terminate; // you can use regular functions too

    s2.f_p();
}

输出

Lambda called
Lambda2 called
terminate called without an active exception

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2021-12-24
    相关资源
    最近更新 更多