C++ Has Block Expressions You Just Need to Write Them in a Funny Way

Rust is an expression language, so everything is an expression, you just sometimes ignore the result so it looks like a statement. This is a feature I really miss in c++ because it allows for some concise patterns. Fortunately there is a fairly readable approximation in c++ 17 via lambdas.

Block expressions

In Rust blocks return values, i.e. I the following is valid code and assigns 2 to result:

let result = { 1 + 1 };

This extends to all expressions, e.g. I can write:

let result = if condition { 1 } else { 2 };

This is useful when e.g. extracting state protected by a lock. The following locks data and then returns a value - usually the value would be based on data but I’m keeping it simple.

let state = {
    data.lock().unwrap();
    return "safe";
};

In c++ I can express the pattern like this:

#include <mutex>

int main()
{
    std::mutex m;
    auto locked_value = [&]() {
        const std::lock_guard<std::mutex> guard(m);
        return "safe";
    }();
}

The main points of interest are

  1. capturing the mutex m as a reference ([&])
  2. invoking the closure immediately (() at the end)

Using a lambda creates a new scope, so at the return we release the lock, and it’s an expression that has to return the correct type on every return.

This scales nicely to larger examples. e.g. in combination with -Wswitch we can avoid break, and also enforce that all switch branches return the same type.

enum Color { PINK, BLUE, GREY };
auto color = Color::PINK;

auto result = [&]() {
    switch (color) {
        case Color::PINK: return "PINK";
        case Color::BLUE: return "BLUE";
        case Color::GREY: return "GREY";
    };
}();