How to make lambdas work with std::nullopt

1.7k Views Asked by At

Background

I have a series of lambdas that perform different checks on the captured variables and return std::nullopt if the check failed. return std::nullopt is the first return statement. Then, if the check succeeded, they go on and compute the value.

Problem

The types of return expressions are not consistent, e.g. std::nullopt_t cannot be converted to std::optional<T>, even though the other way around works. In particular, I'd like the following code to compile and run, printing 2:

#include <functional>
#include <utility>
#include <optional>

int x = 3;

auto lambda = [](){
    if (x == 2)
        return std::nullopt;

    return std::optional(2);
};

#include <iostream>

int main () {
    using return_type = std::invoke_result_t<decltype(lambda)>;
    static_assert(std::is_same<return_type, std::optional<int>>{}, 
                  "return type is still std::nullopt_t");

    std::cout << lambda().value() << '\n';
}

Wandbox Demo.

Thoughts

I believe that I need to use std::common_type<Args...> somewhere, but I can neither enforce presence of it or deduce Args, as it might require language support.

2

There are 2 best solutions below

8
Rakete1111 On BEST ANSWER

Instead of using template type deduction to infer the return type of the lambda, why not explicitly specify that return type?

auto lambda = []() -> std::optional<int> {
    if (x == 2)
        return std::nullopt;

    return 2;
};

std::common_type is commonly with templates, which you don't have.

2
user7860670 On

I suggest to stick with a single return statement and explicitly specified result type without using nullopt at all. It looks somewhat misleading when a function returns either an integer or a nullopt. Especially if the function was longer. Also if value type was something with an explicit constructor then use of emplace allows to avoid typing value type name again.

auto lambda = []()
{
    std::optional<int> result{};
    if(2 != x)
    {
        result.emplace(2);
    }
    return result;
};