【问题标题】:Return from calling function inside lambda从 lambda 中的调用函数返回
【发布时间】:2018-01-07 14:58:15
【问题描述】:

Lambda 是在函数/方法中创建可重用代码而不污染父类的绝佳方式。大多数情况下,它们是 C 风格宏的功能强大的替代品。

但是,宏中有一点我似乎无法用 lambda 复制的语法糖,那就是退出包含函数的能力。例如,如果我需要在检查一系列ints 的范围时返回,我可以使用宏轻松完成:

const int xmin(1), xmax(5);
#define CHECK_RANGE(x) { if((x) < xmin || (x) > xmax) return false; }

bool myFunc(int myint) {
    CHECK_RANGE(myint);
    int anotherint = myint + 2;
    CHECK_RANGE(anotherint);
    return true;
}

显然这是一个过于简单化的例子,但基本前提是我对不同的变量一遍又一遍地执行相同的检查,我认为封装检查和相关出口更具可读性。不过,我知道macros aren't very safe,尤其是当它们变得非常复杂时。但是,据我所知,尝试执行等效的 lambda 需要像这样尴尬的额外检查:

const int xmin(1), xmax(5);
auto check_range = [&](int x) -> bool { return !(x < xmin || x > xmax); };

bool myFunc(int myint) {
    if(!check_range(myint)) return false;
    int anotherint = myint + 2;
    if(!check_range(anotherint)) return false;
    return true;
}

有没有办法用 lambda 做到这一点?还是我错过了一些替代解决方案?

编辑:我知道returning from inside a macro is generally a bad idea 除非significant precautions are taken。我只是想知道这是否可能。

【问题讨论】:

  • 有一点语法糖 - 是的,让代码难以理解。
  • 不,这是不可能的。如果有某种方法可以做到这一点,那就太好了,但目前还没有
  • 在想太多 std::longjmp 能提供什么帮助之前,我会站在贾斯汀一边。
  • @Quentin 我更喜欢宏而不是 longjmp。但实际上,宏唯一的坏处是它不检查类型和语法。宏在您的编译过程之后生成源代码,如果正确使用宏,则没有不安全性。编写宏的方式存在缺陷,例如参数被增量运算符重用..或这些表达式周围没有父级。
  • 但是,有一点语法糖...——这是“thankfully”的一种有趣的拼写方式

标签: c++ c++11 lambda macros syntactic-sugar


【解决方案1】:

你是对的——没有办法从 lambda 内部的调用者那里返回。由于可以从任意调用者内部捕获并存储 lambda 以供以后调用,因此这样做会导致不可预知的行为。

class Foo
{
    Foo(std::function<void(int)> const& callMeLater) : func(callMeLater) {}
    void CallIt(int* arr, int count)
    {
        for (index = count; index--;)
            func(count);
        // do other stuff here.
    }
    std::function<void(int)> func;
};

int main()
{
    auto find3 = [](int arr) 
    {
        if (arr == 3)
            return_from_caller; // making up syntax here.
    };

    Foo foo(find3);
};

【讨论】:

    【解决方案2】:

    有没有办法用 lambda 做到这一点?

    与宏不完全一样,但您的 lambda 可以 throw 一个特殊异常(例如 bool 类型)而不是返回 bool

    auto check_range
       = [](int x) { if ( (x < xmin) || (x > xmax) ) throw bool{false}; };
    

    而函数myFunc()可以拦截这种特殊类型

    bool myFunc (int myint)
     {
       try
        {
          check_range(myint);
          int anotherint = myint + 2;
          check_range(anotherint);
          return true;
        }
       catch ( bool e )
        { return e; }
     }
    

    对于单个check_range() 呼叫,这(我想)是个坏主意;如果你有很多电话,我想会很有趣。

    以下是一个完整的工作示例

    #include <iostream>
    
    constexpr int xmin{1}, xmax{5};
    
    auto check_range
       = [](int x) { if ( (x < xmin) || (x > xmax) ) throw bool{false}; };
    
    bool myFunc (int myint)
     {
       try
        {
          check_range(myint);
          int anotherint = myint + 2;
          check_range(anotherint);
          return true;
        }
       catch ( bool e )
        { return e; }
     }
    
    int main ()
     {
       std::cout << myFunc(0) << std::endl; // print 0
       std::cout << myFunc(3) << std::endl; // print 1
       std::cout << myFunc(7) << std::endl; // print 0
     }
    

    【讨论】:

    • 与其抛出bool,不如抛出一个自定义的struct/class,尤其是从std::exception派生的运行(像std::out_of_rangestd::range_error),然后捕获它并直接使用return false
    • +1 因为这是我正在寻找的解决方案,但接受的答案更好地解决了问题本身。
    【解决方案3】:

    没有比只使用 lambda 的返回值和调用函数的 return 更好的方法了。宏就是为此而生的。

    在 C++ 中,这是退出函数的惯用方式,该函数使用另一个条件来确定是否退出。

    【讨论】:

      【解决方案4】:

      不是 C++11,但人们已经破解了 C++2a 协程基本上可以做到这一点。

      看起来有点像:

      co_await check_range(foo);
      

      co_await 关键字表示在某些情况下,此协程可能会提前返回,结果不完整。在您的情况下,这个不完整的结果将是不可恢复的错误。

      我看到的游戏是可选的,并且需要使用共享 ptr,但在标准化之前情况可能会有所改善。

      【讨论】:

        猜你喜欢
        • 2019-08-21
        • 1970-01-01
        • 1970-01-01
        • 2017-10-14
        • 1970-01-01
        • 2016-11-28
        • 1970-01-01
        • 1970-01-01
        • 2019-04-01
        相关资源
        最近更新 更多