有转发与没有转发的回调
Callback With Forwarding vs Without
假设我有一个触发回调的函数。
template <typename Callback>
void call_callback(Callback&& rvalue_callback)
以下调用代码段有什么区别:
没有转发:
{
rvalue_callback();
}
转发:
{
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 成员函数。
假设我有一个触发回调的函数。
template <typename Callback>
void call_callback(Callback&& rvalue_callback)
以下调用代码段有什么区别:
没有转发:
{ rvalue_callback(); }
转发:
{ 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 成员函数。