[英]what is going on in std::function operator() and std::forward?
我在看std::function
实现及其调用operator()
template<typename Ret, typename... ArgTypes>
Ret function< Ret (ArgTypes...)>::operator()(ArgTypes...args) const
{
// some stuff
return invoker(functor, std::forward<ArgTypes>(args)...);
}
我特别想知道为什么它在这里使用std::forward
? 这有什么与完美转发有关吗? 因为只有当operator()
是带有可变参数模板声明template<typename... Args>
(它不是, 声明是std :: function的部分特化 operator()
才能完成转发。 在这里使用std :: forward的意图是什么? 我很迷惑 :-)?
你是对的,这不是典型的“完美转发”场景。 一个简短的例子可以帮助说明动机。 假设一个带有检测构造函数和析构函数的类型A
:
#include "A.h"
#include <functional>
#include <iostream>
int
main()
{
A a1{1};
A a2{2};
std::function<void(A, A&)> f{[](A x, A& y){}};
f(a1, a2);
}
这将输出:
A(int state): 1
A(int state): 2
A(A const& a): 1
A(A&& a): 1
~A(1)
~A(-1)
~A(2)
~A(1)
说明:
a1
和a2
构造在堆栈上。 然后,当传递给function
调用者时,首先复制a1
以绑定到第一个by-value参数,然后在a1
上调用std::forward<A>
, 将其从by-value参数移动到lambda中。
相反,不需要复制a2
来绑定function
A&
参数,然后调用std::forward<A&>(a2)
,它将a2
转发为左值而不是rvalue,这将绑定到A&
参数拉姆达。
然后事情就会被破坏。 ~A(-1)
表示使用该仪表A
在移动构造的状态下对A
的破坏。
总之,即使ArgTypes
没有像通常的完美转发习惯用法那样推断,我们仍然希望ArgTypes
值ArgTypes
为rvalues,并将ArgTypes
- ArgTypes
为lvalues。 所以std::forward
恰好正是我们想要的。
我觉得你很困惑这里有很多东西。
首先,完美转发与可变参数模板无关。 您可以创建一个包装类,该类包含一个函数,该函数接受一个参数并将其转发给包装对象:
template<typename T>
struct Wrapper {
template<typename Arg>
decltype(auto) test(Arg&& arg) {
return t.test(std::forward<Arg>(arg));
}
T t;
};
注意在没有任何可变参数模板的情况下使用完美转发。 如果t.test
需要仅移动类型作为参数,则不能在没有forward<Arg>(arg)
情况下调用它。
这里发生的第二件事是参数没有被&&
跟随。 添加&&
到ArgTypes
将是一个错误,并且会使某些情况无法编译。 考虑这个简单的情况:
std::function<void(int)> f;
int i = 0;
f(i);
这将无法编译。 如果你向ArgTypes
添加&&
, ArgTypes
每个不引用的参数(例如int
)都将成为调用操作符的右值引用(在我们的例子中是int&&
)。 由于所有参数类型都已在std::function
参数列表中正确限定,因此您希望在调用运算符中接收的内容正是那些未转换的类型。
如果你不使用&&
,为什么你需要std::forward
? 因为即使您不需要推断值类别,仍然需要不将每个参数复制到包含的函数。 如果std::function
的参数之一是int&
,则不想移动它。 但是如果其中一个参数是std::unique_ptr<int>
,你必须移动它! 这正是std::forward
的用途。 只移动应该移动的东西。
std::forward
只是将rvalue引用附加到类型,因此考虑引用折叠规则,它有效地传递引用参数as-is并移动对象参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.