是否有可能捕获lambda类型的异常?

Krz*_*iak 55 c++ lambda exception

虽然最好只抛出从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!
Run Code Online (Sandbox Code Playgroud)

最后一个例子很有趣,因为它可能允许传递一些代码在catch站点执行,而不必定义单独的类或函数.

但是有可能抓住一个lambda(或一个闭包)吗?catch ([]{} e)不起作用.

Sto*_*ica 50

异常处理程序是基于类型匹配,并且完成以匹配一个异常对象到一个处理程序中的隐式转换比在其他上下文中更有限.

每个lambda表达式都引入了一个对于周围范围唯一的闭包类型.所以你的天真尝试无法工作,因为在throw表达式和处理程序中[]{}有一个完全不同的类型!

但你是对的.C++允许您抛出任何对象.因此,如果您事先将lambda显式转换为与异常处理程序匹配的类型,则它将允许您调用该任意可调用对象.例如:

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

这可能有一个有趣的用途,但我要小心不要扔掉那些不是来自的东西std::exception.一个更好的选择可能是创建一个派生自std::exception并可以保持可调用的类型.

  • 当然,我**不会在生产代码中使用它.相反,我正在探索语言的复杂性.虽然我确实尝试通过指向函数来捕获,但是我并没有把它作为`std :: function`抛出并捕获. (7认同)
  • @KrzysiekKarbowiak - 如果类型设计得很仔细,我不明白为什么你不能在生产中这样做.它可能有一些有趣的用途,就像我指出的那样 Ingenuity毕竟已经采用了已知的并以一种新颖的方式使用它:) (4认同)
  • 用于匹配`catch`表达式的确切规则可在[this answer]中找到(/sf/ask/164024591/转换算子).遗憾的是,看起来没有办法捕获lambda,如果它被抛出为`throw [] {};`,因为没有公共基类,并且类型不是指针,因此指针规则不适用. (2认同)

Mic*_*zel 23

C++允许你抛出任何东西.它可以让你抓住你扔的任何东西.当然,你可以扔一个lambda.唯一的问题是,为了捕捉某些东西,你需要知道那种东西的类型或至少是父类型.由于lambdas不是来自一个共同的基础,你必须知道你的lambda的类型来捕捉一个lambda.这个问题的主要问题是每个lambda表达式都会给你一个不同类型的rvalue .这意味着你的throw和catch都需要基于相同的lambda表达式(注意:相同的表达式,而不仅仅是一些看起来完全相同的表达式).我能想到的在某种程度上使这项工作的一种方法是封装lambda的创建以抛出一个函数.这样,您可以在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里尝试一下.

您也可以std::function在其他一些答案中使用类似的建议,这可能是一种更实用的方法.然而,其缺点是

  • 这意味着你实际上不会抛出一个lambda.你扔了一个std::function,这不是你要求的
  • std::function从lambda 创建对象可能会抛出异常


dav*_*ave 6

你可以抛出并抓住一个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";
        }
}
Run Code Online (Sandbox Code Playgroud)

输出:

lambda
catch
Run Code Online (Sandbox Code Playgroud)