有转发与没有转发的回调

Callback With Forwarding vs Without

假设我有一个触发回调的函数。

template <typename Callback>
void call_callback(Callback&& rvalue_callback)

以下调用代码段有什么区别:

  1. 没有转发:

    {
         rvalue_callback();
    }
    
  2. 转发:

    {
         std::forward<Callback>(rvalue_callback)();
    }
    

调用示例:

call_callback([]() { std::cout << "hello world" << std::endl; });

您可以在回答中假设 call_callback 将始终接收 lambda 作为参数,而不是 std::function。

比理论差异更感兴趣的是实际差异。 例如,编译器可以在一种情况下进行性能或优化,但在另一种情况下则不能。

提前致谢,

 ... rvalue_callback

首先,请注意,您正在处理一个 forwarding(或 universal;common non-standard 词)参考在这里,不是右值引用。详情见例:


What is the difference between the following to calling snippets:

You can assume in your answer call_callback will always receive a lambda as a parameter and not an std::function for example.

假设:none。 std::forward 只是一个条件转换(想想 std::move),但是因为你没有在其他地方传递函数参数 rvalue_callback(在这种情况下没有“转发”),并且作为闭包类型lambda 的函数调用运算符不会在其隐式对象参数的 ref- 或 const-qualifiers 上重载其函数调用运算符,您的情况下的条件转换本质上是 no-op (用于排名可行的转换序列略有不同重载,但这里只有一个可行的重载)。

如果您改为传递自定义 class-type 对象并调用成员函数,或者编写了一个重载隐式对象参数的 ref-qualifiers 的仿函数(闭包类型不是这种情况) lambda)那么它可能会有所作为:

#include <iostream>

struct S {
    void f() &  { std::cout << "lvalue\n"; }
    void f() && { std::cout << "rvalue\n"; }
    // This kind of implicit object parameter 
    // overloading would not be present in the
    // closure type of a lambda.
    void operator()() &  { std::cout << "lvalue\n"; }
    void operator()() && { std::cout << "rvalue\n"; }
};

template<typename T>
void memfn1(T&& t) { t(); }

template<typename T>
void memfn2(T&& t) { std::forward<T>(t)(); }

template<typename T>
void funcallop1(T&& t) { t.f(); }

template<typename T>
void funcallop2(T&& t) { std::forward<T>(t).f(); }

int main() {
    S s{};
    memfn1(s);   // lvalue
    memfn2(s);   // lvalue
    memfn1(S{}); // lvalue !!!
    memfn2(S{}); // rvalue

    funcallop1(s);   // lvalue
    funcallop2(s);   // lvalue
    funcallop1(S{}); // lvalue !!!
    funcallop2(S{}); // rvalue
}

然而,这个 转发到 重载 函数参数的 class-type 成员函数。