std::function operator() 和 std::forward 中发生了什么?
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 的部分特化)。 在这里使用 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
作为左值而不是右值转发,这会绑定到 lambda 的A&
参数。
然后事情就被破坏了。~A(-1)
表示使用此检测A
销毁处于移动构造自状态的A
。
总之,即使ArgTypes
不像通常的完美转发习惯用法那样推导,我们仍然希望将按值ArgTypes
作为右值转发,将按引用ArgTypes
作为左值转发。 所以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
,则每个未引用的参数(例如。int
) 将成为调用运算符的右值引用(在我们的例子中为int&&
)。由于所有参数类型都已在std::function
参数列表中正确限定,因此您希望在调用运算符中接收的正是这些类型,而不是转换。
为什么不使用&&
需要std::forward
?因为即使你不需要推断值类别,你仍然不需要将每个参数复制到包含的函数。如果std::function
的参数之一是int&
,则您不想移动它。但是如果其中一个参数是std::unique_ptr<int>
,您必须移动它!这正是std::forward
的目的。只移动应该移动的内容。
std::forward
只是将右值引用附加到类型,因此考虑到引用折叠规则,它有效地按原样传递引用参数并移动对象参数。
- 此测试()中发生了什么意外过程?为什么总是覆盖 ch[0 1 2..]?
- 这C++代码中发生了什么C++(指数函数)
- 哪种方式更快?究竟发生了什么,我们没有看到什么?
- 我正在将一个 std::string 传递给一个 boost 函数,该函数对该类型进行常量引用,但该值发生了变化
- c++问题:给一个变量赋值后,另一个变量发生了变化
- 从"LLONG_MAX 秒"构造 std::chrono::毫秒变量时发生了什么?
- 这个片段中关于 n 在 pc[i] 中的表示发生了什么
- istringstream,num1 和 num2 在这里发生了什么?
- 究竟发生了什么,我们需要在 c++ 中双重调度/访客
- libstdc++的make_shared布局在gcc 4.x和gcc 6.x之间是否发生了变化?
- 我是否能够确定在部署一个程序后发生了什么,我在数组末尾写入?
- 为什么我的 int 在 C++ 程序中间发生了变化?
- 这个 std::vector 构造函数中发生了什么
- 无法运行简单的 std::async 和 std::future 测试程序。错误:"has initializer but incomplete type."发生了什么?
- std::function operator() 和 std::forward 中发生了什么?
- std::assert发生了什么
- std::map的末尾发生了偏移
- 为什么std::vector的构造函数接口在C++11中发生了变化
- 调用 std::map 的运算符 [] 或插入时发生了什么
- 为什么c++ 17中std::函数的operator()发生了变化?