【问题标题】:How do I write a lambda expression that looks like a method?如何编写一个看起来像方法的 lambda 表达式?
【发布时间】:2017-12-02 04:32:25
【问题描述】:

我一直在努力解决这个问题。考虑以下代码(我假设已经定义了前向引用):

// Signature representing a pointer to a method call
typedef
   void (MyClass::*MyMethod)(int);

class MyClass
{
    MyClass();
    void method1(int i);
    void method2(int i);

    void associateMethod(int index, MyMethod m);
} 

鉴于上述情况,构造函数可以执行以下操作:

MyClass::MyClass()
{
   associateMethod(1, &MyClass::method1);
   associateMethod(2, &MyClass::method2);
}

但是,我希望能够调用“associateMethod”,其中第二个参数是匿名方法。但是,以下内容无法编译。

associateMethod(3, [this](int) -> void { /* some code here */ }

我收到一条错误消息,称它们无法从 lambda 转换为 MyMethod。

我想知道 lambda 语法是否需要在某处包含“MyClass”,但随机猜测 lambda 表达式,例如

 MyClass::[this](int) -> void {}

 [this]&MyClass::(int) -> void {}

不要编译。

不胜感激任何指针(没有双关语)

谢谢

【问题讨论】:

  • 为什么不在第一位使用 std::function 而不是原始函数指针?
  • 无论您想在这里做什么,都不能以这种方式完成。类方法需要一个类的实例才能调用它们。这就是使它们成为类方法的原因。 lambda 不绑定到任何类的任何实例。 lambda 本身就是一个匿名类。 lambda 函数始终与唯一的匿名类的实例相关联。也许您想分享您正在尝试解决的真正问题。不,不是关于以这种方式使用 lambda 的问题,而是您认为解决方案是以这种方式使用 lambda 的任何问题。
  • 很难给出一个具体的答案,因为你没有给出产生特定错误的工作代码。但是using MyMethod = std::function<void(int)> 可能是一个解决方案。
  • 嗯,自从我上次使用 C++(多年前)以来,术语是否发生了变化?我的印象是实例成员函数需要一个对象实例,但类方法(使用静态定义)不需要类实例。但是,您的句子,“[a] lambda 不绑定到任何类的任何实例。lambda 本身就是一个匿名类。”实际上完全为我解决了问题。”
  • 我实际上想要做的是注册一个类的选定方法,以便以后可以调用它们,但我希望我可以互换使用“就地”匿名代码(即 lambdas) .

标签: c++ methods lambda syntax


【解决方案1】:

您无法将 lambda 表达式转换为类成员函数指针,并且没有有效的语法使其看起来像一个1

您应该将 MyMethod 声明为 std::function 签名,而不是原始函数指针(如 cmets 中所述):

using MyMethod = std::function<void(int)>;

你可以使用 lambdas then 来初始化这个参数:

MyClass::MyClass()
{
   associateMethod(1, [this](int a) { this->method1(a); });
   associateMethod(2, [this](int a) { this->method2(a); });
}

1)Lambda 函数可以被认为是编译器生成的可调用类,它将捕获作为构造参数,并提供带有您指定的参数和主体的operator()() 重载。因此,不可能有效地转换为原始或成员函数指针。

【讨论】:

  • 感谢您的所有回答。我不想从 lambda 表达式中调用成员函数(method1、method2)。相反,我想要一种方法来注册方法(例如 method1、method2),以便以后可以通过回调调用它们,并且我也希望能够注册“匿名”方法。我意识到我可以通过将方法设为静态并将对象引用作为第一个参数传递来解决此问题,但随后我必须处理诸如访问类中的私有字段之类的问题。
  • 至于 std::function,但除了更简洁的语法(当然是一件好事)之外,我不知道它的行为与传统的 typedef 方法有什么不同。
【解决方案2】:

user0042 的答案似乎是要走的路,但为了完整起见,值得一提的是,在 C++17 中,无捕获 lambda 有一个 constexpr 转换运算符它们的函数指针类型,因此您应该(*)能够通过以下方式将这样的 lambda 转换为成员函数指针:

// ...
void associateMethod(int index, MyMethod m);

template<typename F>
void associateMethod(int index, F m) {
  associateMethod( index,
    static_cast<MyMethod>(
      &MyClass::bindFun< static_cast<void(*)(MyClass*,int)>(m) >
    ) );
}

private:

template<auto F>
void bindFun(int x){ (*F)(this,x); }

// to be used like 
x.associateMethod(0,[](MyClass* this_, int x){ this_->method1(x+1); });

(*) 可悲的是,这在 clang 中编译,但 gcc 拒绝编译它(我要问一个关于这个的问题,你可以找到它here)。

【讨论】:

  • 很有趣,除了我没有尝试从 lambda 表达式调用 method1。我希望能够同时注册成员函数和 lambda 表达式,以便以后可以回调任何一种。
  • @David,是的,这在理论上确实允许这样做;我称之为this_->method1只是为了举个例子......
  • 不知道为什么它回答了我提出的问题时得到了零票。我目前无法利用该答案(我们仍在使用 C++14),但从智力上来说,知道如何完成是件好事。
  • @david BTW,我确认这是有效的 C++17 代码,虽然目前只有 clang>=5.0 支持它(gcc 有一个错误,请参阅答案中的链接以获取更多详细信息) .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 1970-01-01
  • 2017-01-24
  • 1970-01-01
  • 2010-11-07
  • 1970-01-01
相关资源
最近更新 更多