如何从lambda中的函数返回?

时间:2011-09-03 07:00:22

标签: c++ lambda return c++11 control-flow

考虑以下玩具代码以确定范围是否包含元素:

template<typename Iter, typename T>
bool contains1(Iter begin, Iter end, const T& x)
{
    for (; begin != end; ++begin)
    {
        if (*begin == x) return true;
    }
    return false;
}

(是的,我知道,标准库中已有完美的算法,但这不是重点。)

我如何用for_each和lambda写同样的东西?以下不起作用......

template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
    std::for_each(begin, end, [&x](const T& y) {
        if (x == y) return true;
    });
    return false;
}

...因为那只会从lambda返回,而不是从函数返回。

我是否必须抛出异常以摆脱lambda?同样,对于这个特定问题可能有十几个更好的解决方案,根本不涉及lambdas,但这不是我要求的。

7 个答案:

答案 0 :(得分:7)

如果您想提前结束循环,

std::for_each不是您应该使用的算法。您似乎想要std::find_if或类似的东西。您应该使用最适合您任务的算法,而不仅仅是您熟悉的算法。


如果你真的,真的真的必须尽早从算法“返回”,你可以 -

警告:以下是真正的真的坏主意,你几乎不应该这样做。实际上,查看代码可能会让你的脸变得脆弱。 你已被警告过了!

抛出异常:

bool contains2(Iter begin, Iter end, const T& x)
{
  try {
    std::for_each(begin, end, [&x](const T& y) {
        if (x == y)
          throw std::runtime_error("something");
    });
  }
  catch(std::runtime_error &e) {
    return true;
  }
  return false;
}

答案 1 :(得分:7)

  

我如何用for_each和lambda编写相同的东西?

你不能(不考虑例外)。你的函数与for-each循环(=一种映射)并不是同构的,它就像那样简单。

相反,您的功能是通过缩减来描述的,因此如果您想使用高阶函数来替换它,请使用缩小而不是地图。

如果 C ++ 具有适当的通用reduce,那么您的算法将如下所示:

template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
    return stdx::reduce(begin, end, [&x](const T& y, bool accumulator) {
        return accumulator or x == y;
    });
}

当然,如果缩减适当地专门用于布尔结果值,这只会提前退出,以便短路。

唉,就我看来,C ++并没有提供这样的功能。有accumulate但不会短路(它不能--C ++不知道内部 lambda的操作是短路的,而且它不是递归实现的)。

答案 2 :(得分:2)

Lambdas是错误的抽象层次,因为它们的行为很像函数 - 至少在控制流程方面,这在这里很重要。你不希望某些东西像'封装'那样作为一个函数(或程序编程的程序),它在C ++中只能直接返回或抛出异常。在我看来,任何颠覆这种行为的企图都应该被认为是病态的 - 或者至少不应该伪装成一个程序。

对于执行流程的细粒度控制,诸如协同程序之类的东西可能是更适合的抽象级别和/或原始级别。不过,我担心最终结果与使用std::for_each完全不同。

答案 3 :(得分:2)

使用自定义算法:

template<class I, class F>
bool aborting_foreach(I first, I last, F f) {
  while(;first!=last;++first) {
    if(!f(*first))
      return false;       
  }
  return true;
}

好的,这实际上是std :: all_of,但你明白了。 (参见“减少答案”)。如果您的函数需要返回某种类型,则可能需要使用某种变体类型:

// Optional A value
template<class A>
class maybe {
  // ...
};

// Stores either a A result of a B "non local return"
template<class A, class B>
class either {
  …
};

查看相应的Haskell类型。你可以使用C ++ 01“unrestricted union”来干净地实现它。

执行非本地退出的一种干净方法是使用continuation但是你没有在C ++中使用它们。

答案 4 :(得分:2)

使用std::any_of

template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
    const bool contains = std::any_of(begin, end, [&x](const T& y)
    {
        return x == y;
    });

    return contains;
}

答案 5 :(得分:0)

在这种情况下,lambda就像从给定函数contains2()调用的任何其他函数一样。从其他函数返回并不意味着您从给定函数返回。因此,不可能,这就是设计的方式。

对于给定示例的模式,抛出异常是不必要的开销。我会在lambda中设置一个bool变量而不是return(并设置begin = end;)。可以检查此bool是否从给定函数contains2()返回。

答案 6 :(得分:0)

正如您和其他人所指出的那样for_each不适合在这里使用。没有办法打破for_each循环 - 除了异常(双关语) - 你必须完全运行它。

template<typename Iter, typename T> 
bool contains2(Iter begin, Iter end, const T& x) 
{ 
    bool tContains = false;
    std::for_each(begin, end, [&](const T& y) mutable { 
        tContains = tContains || x == y; 
    });
    return tContains; 
}