I am using lambdas to initialize some const variables as described in the core c++ guidelines here. In short, the idiom looks like this

const auto a = [&]() {
    MyType a;
    // complex initialization
    return a;
}();

The problem arises when the initialization can throw an exception that can only be handled outside of the lambda, eg because it must cause the function to return early. I want to be able to do something that looks like this

try {
    const auto a = [&]() {
        MyType a;
        // complex initialization
        return a;
    }();
} 
catch { /* doesn't matter what */ } {
    // Somehow handle it
    // then return or throw
}
// use a

except that I want the object to be usable after the try-catch block when an exception is not thrown.

I could refactor the function so that all code that depends on successful initialization of the object is inside the try block but this scales horribly with multiple objects to initialize.

try {
    const auto a = ...;
    try {
        const auto b = ...;
        try...
        catch...
    }
    catch {...} {
        ...;
    }
catch {...} {
    ...;
}

On the other hand, moving the try-catch block inside the lambda restricts what I can do to handle it. eg it does not allow me to instantly return, break or continue as far as I can tell. It almost feels like the logical solution would be to interleave the assignment with the try block (eg const auto a = try [&]()...) but it does not seem to be supported by the anguage. Am I correct in that this is not supported and if so is there another known idiom that would result in the behavior I need?

Note: I am asking for situations where it is important for the variable to be const or where default construction followed by assignment is impossible so the obvious solution of default constructing outside the try-block and assigning the value inside it is not viable.

1

There are 1 best solutions below

0
On

It is honestly no different from simple initialization which also may throw. Try not to micro manage your exceptions. Their power is in allowing you to code freely without worrying about errors every single statement. Put all of the code that does a complete job (or operation) in your try{}catch(){}. An exception should abort the whole procedure, not just a single initialization.

try
{
    const auto a = [&]() {
        MyType a;
        // complex initialization
        return a;
    }();

    const auto b = "something else that may throw"s;

    const auto c = a_throwing_function();

    // Now do stuff with a, b and c safe in the knowledge that
    // they are properly constructed and valid

    // ... etc ...
}
catch(std::exception const& e)
{
    // abort the entire operation
}