是否可以在 C++ 中使用 lambda 而不是类?

时间:2020-12-20 16:19:39

标签: c++ lambda

我最近一直在阅读一本为 Java 编写的设计模式书,但我实际上对将这些技术应用于 C++ 感兴趣,因此我正在将示例从 Java“翻译”到 C++。我发现在 Java 中,可以使用 lambda 表达式作为函数的参数而不是对象(类的实例)。以我目前的 C++ 知识,我还没有找到使用 C++ 做等效或类似事情的方法,我什至不知道这是否可行,所以我在这里试一试。

在 Java 中:

public class Light {
    public void on()
    {
        //whatever
    }
    public void off()
    {
        //whatever
    }
}

public interface Command {
    public virtual void execute();
};

public class RemoteControl {

    Command m_command;

    public RemoteControl() {}

    public void setCommand(Command command)
    {
        m_command = command;
    }
}

书上说,在Java中,鉴于接口的特性(只有一个方法),可以使用带有接口中声明的方法签名的lambda来代替接口的“实例”,例如:

RemoteControl remoteControl;
Light livingRoomLight;
remoteControl.setCommand( ()->{livingRoomLight.on();} );

我可以在 C++ 中做些什么来实现类似的目标?

我当前的 C++ 代码:

class Light
{
public:
    Light() {}

    void on()
    {
        //whatever
    }

    void off()
    {
        //whatever
    }
};

class Command  
{
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
};

class RemoteControl
{
private:
    std::shared_ptr<Command> m_command = {nullptr};

public:

    RemoteControl() {}

    void setCommand(std::shared_ptr<Command> command)
    {
        m_command = command;
    }
};

是否可以在 C++ 中使用 lambda 表达式而不是指向 Command 实例的指针?

类似于:

std::unique_ptr<RemoteControl> remoteControl = std::make_unique<RemoteControl>();
std::shared_ptr<Light> livingRoomLight = std::make_shared<Light>();

remoteControl->setCommand( [livingRoomLight](){ livingRoomLight->on(); );

RemoteControl* remoteControl = new RemoteControl;
Light* livingRoomLight = new Light;

std::function<void()> livingRoomLightOn = [livingRoomLight]() { livingRoomLight->on(); };

remoteControl->setCommand( &livingRoomLightOn );

2 个答案:

答案 0 :(得分:3)

有可能,但你的语法是错误的。以下是如何操作的示例:

#include <iostream>
#include <functional>

class Light
{
public:
    void on () { std::cout << "Light on\n"; }
    void off () { std::cout << "Light of\n"; }
};

class RemoteControl
{
public:
    void setCommand (std::function <void ()> command)
    {
        m_command = command;
    }
    
    void performCommand () { m_command (); }

private:
    std::function <void ()> m_command;
};

int main ()
{
    Light livingRoomLight;
    RemoteControl remoteControl;

    remoteControl.setCommand ([&livingRoomLight] () { livingRoomLight.on (); });
    remoteControl.performCommand ();
}

输出:

Light on

请注意,livingRoomLight 是通过引用传递给 lambda 的,以便它可以修改原始对象。

答案 1 :(得分:1)

是的,您可以传递一个 lambda1,但是由于每个 lambda 表达式都会创建一个新的唯一类型(并且您不知道该类型的名称),因此您必须2< /sup> 使用模板参数作为 lambda 的类型:

template <class Command>
class RemoteControl {
    std::function<void()> command;
public:
    RemoteControl(Command const &command) : command(command) {}
    void operator() { command(); }
};

int main() { 
    Light light;
    RemoteControl turnOn([&] mutable { light.on(); });
    RemoteControl turnOff([&] mutable { light.off(); });

    // ...

    turnOn(); // invoke the remote control to turn on the light

    // ...
    turnOff(); // invoke the other remote control to turn off the light
}

尽管如此,我还是敦促谨慎使用。特别是,您基本上使用了一种相当复杂、迂回的方式来创建等效于将 Light 作为一个简单变量,并使用几个自由函数来操作它的东西:

bool light; // probably not the real type, but it'll do for now

void turnOn() { light = true; }
void turnOff() { light = false; }

Java 不允许自由函数,所以你必须使用这种迂回的方式来模仿它们。 C++ 不会以这种方式限制您,因此如果您可以控制当前包含在 Light 类中的代码,并且您希望它像一个由自由函数操作的简单变量一样,那么您可能应该这样做直接说。


  1. 好吧,从技术上讲,您传递的是“闭包”。 “lambda 表达式”基本上是指源代码中的语法。由 lambda 表达式创建的“东西”是闭包——但除非你特别喜欢肛门,否则不要担心——大多数人会像你一样使用“lambda”。
  2. 好吧,从技术上讲,您不必这样做。您可以使用 std::function 代替 - 但作为规则,您应该为参数本身使用模板类型。 std::function 应仅用于存储 lambda(如果您需要这样做)。
相关问题