虽然只抛出从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!
最后一个例子很有趣,因为它可能允许传递一些代码在捕获点执行,而不必定义单独的类或函数。
但是,是否有可能捕获lambda(或闭包)?catch ([]{} e)
不起作用。
更新(2022年11月14日):
参见here我自己的答案,考虑到C++20的特性。
5条答案
按热度按时间umuewwlo1#
异常处理程序是基于类型进行匹配的,并且为将异常对象与处理程序匹配而进行的隐式转换比在其他上下文中受到更多限制。
每个lambda表达式都引入了一个闭包类型,这个闭包类型对于周围的作用域是唯一的。所以你的天真尝试是行不通的,因为
[]{}
在throw表达式和句柄中有一个 * 完全不同的类型 *!但是你是对的。C++允许你抛出任何对象。所以如果你事先显式地将lambda转换成一个与异常处理程序匹配的类型,它将允许你调用那个任意的可调用对象。例如:
这可能会有一些有趣的实用程序,但是我警告不要抛出不是从
std::exception
派生的东西。一个更好的选择可能是创建一个从std::exception
派生的类型,并且可以保存一个可调用对象。im9ewurl2#
C++允许你抛出任何东西。它允许你捕捉你抛出的任何东西。当然,你可以抛出一个lambda。唯一的问题是,要捕捉某个东西,你需要知道它的类型,或者至少知道它的父类型。由于lambda不是从公共基派生的,你必须知道你的lambda的类型才能捕捉lambda。2主要的问题是每个lambda表达式都会给予你一个不同类型的右值。这意味着throw和catch需要基于同一个lambda表达式(注意:同样的表达式,而不仅仅是看起来完全一样的表达式)。我能想到的在某种程度上实现这一点的一种方法是将lambda的创建封装到一个函数中。这样,你就可以在
throw
表达式中调用该函数,并使用该函数的返回类型将类型推导为catch
:在这里试试。
您也可以像其他一些答案中建议的那样使用
std::function
,这可能是一种更实用的方法。std::function
,这并不是你真正想要的😉std::function
对象会引发异常wpcxdonn3#
您可以投掷和接住
std::function
:输出量:
svujldwt4#
lambda是唯一的匿名类型。命名lambda示例的类型的唯一方法是将其存储在变量中,然后对该变量类型执行
decltype
。有几种方法可以捕获抛出的lambda。
在这种情况下,除了再次抛出它之外,您无法使用它。
无状态lambda可以被转换为函数指针。
您可以将其转换为
std::function
。std::function
的缺点是它会为较大的lambda分配堆,这在理论上可能会导致抛出。我们可以消除堆分配:
现在您可以执行以下操作:
callable
是比std::function
“重量更轻”的类型擦除,因为它不会导致分配新的堆存储器。Live example。
jhdbpxl95#
现在是2022年,当前的C标准是C20,它给了我们一些更有趣的机会,这是我在看了这个演讲后意识到的:C++ Lambda Idioms,尤其是this part。
Lambda现在是默认可构造的,并且允许在未赋值的上下文中使用(例如decltype),这允许我们这样做:
Try it out on Compiler Explorer。
因此,目前看起来简短的答案是是的,可以捕获lambda类型的异常。