пример - стандартные исключения c++




Можно ли поймать исключение типа лямбда? (3)

C ++ позволяет бросать все что угодно. И это позволяет вам ловить все, что вы бросаете. Можно, конечно, кинуть лямбду. Единственная проблема заключается в том, что для того, чтобы поймать что-либо, вам нужно знать тип или хотя бы родительский тип этого чего-либо. Поскольку лямбды не происходят из общей базы, вы должны знать тип вашей лямбды, чтобы поймать лямбду. Основная проблема заключается в том, что каждое лямбда-выражение даст вам значение определенного типа . Это означает, что и ваш бросок, и ваш улов должны основываться на одном и том же лямбда-выражении (примечание: одно и то же выражение, а не просто какое-то выражение, которое выглядит одинаково). Один из способов сделать эту работу в некоторой степени - заключить инкапсуляцию создания лямбда-функции в функцию. Таким образом, вы можете вызвать функцию в своем выражении throw и использовать возвращаемый тип функции, чтобы определить тип для catch :

#include <utility>

auto makeMyLambda(int some_arg)
{
    return [some_arg](int another_arg){ return some_arg + another_arg; };
}

void f()
{
    throw makeMyLambda(42);
}

int main()
{
    try
    {
        f();
    }
    catch (const decltype(makeMyLambda(std::declval<int>()))& l)
    {
        return l(23);
    }
}

Попробуйте это here .

Вы также можете просто использовать std::function как предложено в некоторых других ответах, что потенциально является более практичным подходом. Недостатки этого, однако, будут

  • Это означает, что вы на самом деле не выбрасываете лямбду. Вы бросаете функцию std::function , которая не совсем то, что вы просили 😉
  • Создание объекта std::function из лямбды может вызвать исключение

Несмотря на то, что рекомендуется генерировать только исключения типов, производных от std::exception , C ++ позволяет генерировать все что угодно. Все приведенные ниже примеры действительны на C ++:

throw "foo";  // throws an instance of const char*
throw 5;      // throws an instance of int

struct {} anon;
throw anon;   // throws an instance of not-named structure

throw []{};   // throws a lambda!

Последний пример интересен, поскольку он потенциально позволяет передавать некоторый код для выполнения на узле перехвата без определения отдельного класса или функции.

Но возможно ли вообще поймать лямбду (или замыкание)? catch ([]{} e) не работает.


Вы можете бросить и поймать std::function :

#include <iostream>
#include <functional>

void f() {
        throw std::function<void(void)>([]{std::cout << "lambda\n"; });
}

int main()
{
        try{ f(); }
        catch( std::function<void(void)> &e)
        {
                e();
                std::cout << "catch\n";
        }
}

Выход:

lambda
catch

Обработчики исключений сопоставляются на основе типа, и неявные преобразования, выполняемые для сопоставления объекта исключения с обработчиком, более ограничены, чем в других контекстах.

Каждое лямбда-выражение представляет тип замыкания, уникальный для окружающей области видимости. Так что ваша наивная попытка не может сработать, потому что []{} имеет совершенно другой тип в выражении throw и обработчике!

Но вы правы. C ++ позволяет бросать любой объект. Таким образом, если вы явно преобразуете лямбда-код в тип, соответствующий обработчику исключений, это позволит вам вызвать этот произвольный вызываемый объект. Например:

try {
    throw std::function<void()>{ []{} }; // Note the explicit conversion
} catch(std::function<void()> const& f) {
    f();
}

Это может иметь интересную полезность, но я бы предостерег от использования объектов, не производных от std::exception . Лучшим вариантом, вероятно, было бы создание типа, который наследуется от std::exception и может содержать вызываемый объект.







lambda