Reputation: 345
I have recently been reading a design patterns book written for Java, but I am actually interested in applying the techniques to C++, so I am "translating" the examples from Java to C++. I found out that in Java, it is possible to use a lambda expression as a parameter of a function instead of an object (instance of a class). With my current C++ knowledge, I have not found a way to do something equivalent or similar using C++, I don't even know if that is possible, so I give it a try here.
In 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;
}
}
According to the book, in Java, given the characteristics of the interface (just one method), it is possible to use a lambda with the signature of the method declared in the interface instead of an "instance" of the interface, eg:
RemoteControl remoteControl;
Light livingRoomLight;
remoteControl.setCommand( ()->{livingRoomLight.on();} );
What can I do in C++ to achieve something similar?
My current C++ code:
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;
}
};
Is it possible in C++ to use a lambda expression instead of a pointer to a Command
instance?
Something like:
std::unique_ptr<RemoteControl> remoteControl = std::make_unique<RemoteControl>();
std::shared_ptr<Light> livingRoomLight = std::make_shared<Light>();
remoteControl->setCommand( [livingRoomLight](){ livingRoomLight->on(); );
or
RemoteControl* remoteControl = new RemoteControl;
Light* livingRoomLight = new Light;
std::function<void()> livingRoomLightOn = [livingRoomLight]() { livingRoomLight->on(); };
remoteControl->setCommand( &livingRoomLightOn );
Upvotes: 2
Views: 445
Reputation: 490068
Yes, you can pass a lambda1, but since each lambda expression creates a new, unique type (and you don't know the name of that type) you have2 to use a template parameter for the type of the 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
}
I would urge that this be used with caution though. In particular, you're basically using a rather complex, roundabout way of creating something equivalent to just having the Light
as a simple variable, with a couple of free functions to manipulate it:
bool light; // probably not the real type, but it'll do for now
void turnOn() { light = true; }
void turnOff() { light = false; }
Java doesn't allow free functions, so you have to use this roundabout way to imitate them. C++ doesn't restrict you that way, so if you have control of the code that's currently embodied in the Light
class, and you want it to act like a simple variable that's manipulated by free functions, then you should probably do that directly.
std::function
instead--but as a rule, you should use a template type for the parameter itself. std::function
should be used only for storing the lambda (if you need to do that).Upvotes: 1
Reputation: 25388
It's possible, but your syntax is wrong. Here's an example of how to do it:
#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 ();
}
Output:
Light on
Note that livingRoomLight
is passed to the lambda by reference so that it can modify the original object.
Upvotes: 3