Home » Code » Using C++11 Lambdas As Qt Slots

Using C++11 Lambdas As Qt Slots

posted by Andy Maloney

Qt Library

Qt Framework

I have an old codebase I started writing using the Qt 3.x framework—a little while before Qt4 was released. It’s still alive! I still work on it, keeping up-to-date with Qt and C++ as much as possible, and I still ship the product. Over the years I have moved the codebase along through Qt4 to Qt5, and on to compilers supporting C++11. One of the things I’ve sometimes found a little excessive is declaring slots for things that are super short and won’t be reused.

Here’s a simplified example from my older code that changes the title when a project is “dirty” (needs to be saved):

(Aside: If you are wondering about my naming convention, I developed a style when using Qt early on (c. 2000) where signals are named signalFoo() and slots are named slotFoo(). This way I know at a glance what the intent is. If I’m about to modify a slot function I might take an extra minute to look around since most IDEs can’t tell syntactically where it’s used in a SLOT() macro. In this case you have to search for it textually.)

Thanks to C++11 lambdas and Qt’s ongoing evolution, these short slots can be replaced by a more succinct syntax. This avoids having to declare a method in your class declaration and shortens your implementation code. Both desirable goals!

Let’s take a look.

Getting rid of SIGNAL() and SLOT() macros

The first really nice thing about Qt5 is that we can move away from the classic SIGNAL() and SLOT() macros to using method pointers.

This does several things for us:

  • relieves us of having to lookup and fill in the parameters for the methods
  • shortens your code
  • IDEs can identify the methods used in a connect call when you search for uses of a method
  • allows implicit conversion of arguments
  • catches problems at compile time rather than runtime

This last one is the most important. If the two methods you are trying to connect are mismatched it simply won’t compile. I can say with authority that problems found at compile time can save you many, many hours of debugging!

So if we apply this idea to our example, the connect() call becomes:

Already an improvement!

What are these lambda things anyways?

A lambda is essentially a fancy name for an anonymous function. These are typically short and can be useful for things like this slot mechanism where the code may not be re-usable, or for callbacks.

The C++11 specification for a lambda function looks like this:

[capture](parameters) -> return_type { function_body }

Looks simple, but when you dig into the details, they can be rather complicated.

The capture part defines how variables in the current scope are captured for the function_body to use. Put another way, it defines what variables are made available to the lambda function body from the current scope.

The one we’ll use here is [=] which says to capture all variables from the local scope by value. You can also capture nothing ([]), capture all variables in the scope by reference ([&]), capture just the members of the class ([this]), or capture individual variables ([foo, &bar]) – or some combination.

It also takes optional parameters which is how we’ll pass signal parameters to the lambda (see below).

return_type and function_body are standard C++.

If we apply this to our example, we get something like this:

Here we connect our signal to an anonymous function which has access to the variables in the current scope. We leave out the return type so void is inferred.

(This is a very quick intro to lambdas. You can find a much more in-depth description at cppreference.com and in this post.)

Our final result

Here’s our result if we put all that together. We got rid of the extra declaration in the header and the extra function in the implementation. Much nicer.

What about the arguments?

As I mentioned earlier, lambdas can also take arguments, so you can pass the parameters from the signal into the lambda function. For example, I recently ran into an issue where I needed to accept a QRectF, adjust it, and send it on again. I did it like this:

You’ll note that I am also using mRatio and mView from the enclosing class. Because we captured using [=], the body of the lambda function has access to variables in the enclosing scope.

Because I’m only using the members of this, I could have captured using [this] instead:

I’m not aware of any advantages of one over the other – except that maybe reducing the scope the lambda function body has access to may be desirable. If you have any opinions on this, please leave a comment!


Depending on how you work, using lambdas may make testing more difficult. Having separate methods you can call in testing might be more desirable.

It’s also tempting to use lambdas in lots of places. It’s pretty powerful. I think, however, that restricting it to simple things like the above—replacing short one-use slots, adjusting data before passing it along, or for simple callbacks—will be the most maintainable in the long run. Until we work with them and live with the resulting code for a few years we won’t know…

Hope that helps someone out there on the information superhighway!

Leave a Reply

Your email address will not be published. Required fields are marked *