37

C++11 模式下的 GCC 4.7 让我定义了一个采用 lambda 的函数,有两种不同的方式:

// by value
template<class FunctorT>
void foo(FunctorT f) { /* stuff */ }

和:

// by r-value reference
template<class FunctorT>
void foo(FunctorT&& f) { /* stuff */ }

但不是:

// by reference
template<class FunctorT>
void foo(FunctorT& f) { /* stuff */ }

我知道我可以取消对函数的模板化,而只使用 std::functions ,但是foo它很小而且是内联的,我想给编译器最好的机会来内联对它内部的 f 的调用。在前两个中,如果我特别知道我正在传递 lambda,这对于性能来说更可取,为什么不允许将 lambda 传递给最后一个?

4

3 回答 3

33

FunctorT&&是一个通用参考,可以匹配任何东西,而不仅仅是右值。这是在 C++11 模板中传递内容的首选方式,除非您绝对需要副本,因为它允许您使用完美转发。通过 访问该值,如果它是以前的,它将再次std::forward<FunctorT>(f)创建f一个右值,否则将其保留为左值。在此处阅读有关转发问题的更多信息,std::forward在此处std::forward阅读有关实际工作原理的分步指南。也是一本有趣的读物。

FunctorT&只是一个简单的左值引用,您不能将临时变量(lambda 表达式的结果)绑定到它。

于 2012-09-22T23:56:09.920 回答
5

当你创建一个 lambda 函数时,你会得到一个临时对象。您不能将临时绑定到非常量左值引用。实际上,您不能直接创建引用 lambda 函数的左值。

T&&当你使用函数的参数类型声明你的函数模板时,T const&如果你将一个const对象传递给函数,T&如果你传递一个非常量左值对象给它,T如果你传递一个临时对象。也就是说,当传递一个临时函数声明时,函数声明将采用一个 r 值引用,该引用可以在不移动对象的情况下传递。当通过值显式传递参数时,临时对象在概念上被复制或移动,尽管此复制或移动通常被省略。如果您只将临时对象传递给您的函数,则前两个声明会做同样的事情,尽管第一个声明可能会引入移动或复制。

于 2012-09-22T23:59:28.300 回答
3

这是一个很好的问题——第一部分:按值传递或使用转发。我认为第二部分(FunctorT&作为论点)已经得到了合理的回答。

我的建议是:仅在函数对象已知时才使用转发,以修改其闭包(或捕获列表)中的值。最好的例子:std::shuffle。它需要一个统一随机数生成器(一个函数对象),每次调用生成器都会修改其状态。函数对象被转发到算法中。

在所有其他情况下,您应该更喜欢按值传递。这不会阻止您通过引用捕获本地变量并在您的 lambda 函数中修改它们。这将像您认为的那样工作。正如 Dietmar 所说,复制不应该有任何开销。内联也将适用,并且可以优化引用。

于 2012-09-23T02:50:21.440 回答