代码之家  ›  专栏  ›  技术社区  ›  Krzysiek Karbowiak

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

  •  55
  • Krzysiek Karbowiak  · 技术社区  · 6 年前

    而只抛出从 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站点执行,而无需定义单独的类或函数。

    但是,是否有可能抓住一个lambda(或关闭)? catch ([]{} e) 不起作用。

    4 回复  |  直到 6 年前
        1
  •  51
  •   StoryTeller - Unslander Monica    6 年前

    每个lambda表达式都引入一个闭包类型,该闭包类型对于周围的作用域是唯一的。所以你天真的尝试是行不通的,因为 []{} 有一个 完全不同的类型

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

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

    这可能有一个有趣的实用程序,但我要提醒大家不要抛出非源代码的东西 std::exception . 更好的选择可能是创建从 异常

        2
  •  24
  •   Michael Kenzel    6 年前

    C++允许你投掷任何东西。它可以让你抓住你扔的任何东西。当然,你可以扔一只羔羊。唯一的问题是,要捕获某个对象,您需要知道该对象的类型或至少父类型。由于lambda不是从公共基派生的,因此必须知道lambda的类型才能捕获lambda。主要问题是,每个lambda表达式都会给您一个 distinct type . 这意味着您的投掷和捕获都需要基于相同的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);
        }
    }
    

    here .

    你也可以使用 std::function 正如其他一些答案中所建议的,这可能是一种更实际的方法。然而,这样做的缺点是

        3
  •  7
  •   perivesta    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";
            }
    }
    

    lambda
    catch
    
        4
  •  1
  •   Yakk - Adam Nevraumont    6 年前

    lambda是唯一的匿名类型。命名lambda实例类型的唯一方法是将其存储在变量中,然后执行 decltype

    有几种方法可以让你抓到一只被抛出的羔羊。

    try  {
      throw []{};
    } catch(...) {
    }
    

    在这种情况下,你不能使用它,除了再次扔它。

    try  {
      throw +[]{};
    } catch(void(*f)()) {
    }
    

    try  {
      throw std::function<void()>([]{});
    } catch(std::function<void()> f) {
    }
    

    您可以将其转换为 std::function . 不利因素 std::函数 它的堆分配给更大的lambda,这在理论上可能导致它抛出。

    我们可以消除堆分配:

    template<class Sig>
    struct callable;
    
    template<class R, class...Args>
    struct callable<R(Args...)> {
      void* state = nullptr;
      R(*action)(void*, Args&&...) = nullptr;
      R operator()(Args...args) const {
        return action( state, std::forward<Args>(args)... );
      }
    };
    
    template<class Sig, class F>
    struct lambda_wrapper;
    template<class R, class...Args, class F>
    struct lambda_wrapper<R(Args...), F>
    :
      F,
      callable<R(Args...)>
    {
      lambda_wrapper( F fin ):
        F(std::move(fin)),
        callable<R(Args...)>{
          static_cast<F*>(this),
          [](void* self, Args&&...args)->R {
            return static_cast<R>( (*static_cast<F*>(self))( std::forward<Args>(args)... ) );
          }
        }
      {}
      lambda_wrapper(lambda_wrapper && o):
        F(static_cast<F&&>(o)),
        callable<R(Args...)>( o )
      {
        this->state = static_cast<F*>(this);
      }
      lambda_wrapper& operator=(lambda_wrapper && o)
      {
        static_cast<F&>(*this) = (static_cast<F&&>(o));
        static_cast<callable<R(Args...)>&>(*this) = static_cast<callable<R(Args...)>&>( o );
        this->state = static_cast<F*>(this);
      }
    };
    
    template<class Sig, class F>
    lambda_wrapper<Sig, F> wrap_lambda( F fin ) {
      return std::move(fin);
    }
    

    try {
      throw wrap_lambda<void()>([]{});
    } catch( callable<void()> const& f ) {
    }
    

    callable “较轻重量”类型的擦除是否比 std::函数

    Live example .