简体   繁体   English

在std :: function operator()和std :: forward中发生了什么?

[英]what is going on in std::function operator() and std::forward?

I was looking at the std::function implementation and its call operator() 我在看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)...);
}

I was particularly wondering, why it uses std::forward here? 我特别想知道为什么它在这里使用std::forward Does this anything have to do with perfect forwarding? 这有什么与完美转发有关吗? Because perfect forwarding could only be done if operator() is a template with a variadic template declaration template<typename... Args> (which it is not, the declaration is a partial specialization of std::function ). 因为只有当operator()是带有可变参数模板声明template<typename... Args> (它不是, 声明是std :: function的部分特化 operator()才能完成转发。 What is the intention of using std::forward here? 在这里使用std :: forward的意图是什么? I am confused :-)? 我很迷惑 :-)?

You are correct that this is not your typical "perfect forwarding" scenario. 你是对的,这不是典型的“完美转发”场景。 A brief example can help illustrate the motivation. 一个简短的例子可以帮助说明动机。 Assume a type A with instrumented constructors and destructor: 假设一个带有检测构造函数和析构函数的类型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);
}

This will output: 这将输出:

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)

Explanation: 说明:

a1 and a2 are constructed on the stack. a1a2构造在堆栈上。 Then when passed into the function invoker, a1 is first copied to bind to the first by-value parameter, and then std::forward<A> is called on a1 which moves it from the by-value parameter into the lambda. 然后,当传递给function调用者时,首先复制a1以绑定到第一个by-value参数,然后在a1上调用std::forward<A>其从by-value参数移动到lambda中。

In contrast, a2 need not be copied to bind to the function A& parameter, and then std::forward<A&>(a2) is called, which forwards a2 as an lvalue instead of rvalue, and this binds to the A& parameter of the lambda. 相反,不需要复制a2来绑定function A&参数,然后调用std::forward<A&>(a2) ,它将a2转发为左值而不是rvalue,这将绑定到A&参数拉姆达。

Then things get destructed. 然后事情就会被破坏。 The ~A(-1) indicates the destruction of an A in a move-constructed-from state with this instrumented A . ~A(-1)表示使用该仪表A在移动构造的状态下对A的破坏。

In summary, even though ArgTypes isn't deduced as in the usual perfect forwarding idiom, we still want to forward by-value ArgTypes as rvalues, and by-reference ArgTypes as lvalues. 总之,即使ArgTypes没有像通常的完美转发习惯用法那样推断,我们仍然希望ArgTypesArgTypes为rvalues,并将ArgTypes - ArgTypes为lvalues。 So std::forward just happens to do exactly what we want here. 所以std::forward恰好正是我们想要的。

I think you are confused by many things here. 我觉得你很困惑这里有很多东西。

First, perfect forwarding has nothing to do with variadic templates. 首先,完美转发与可变参数模板无关。 You could create a wrapper class that has a function that takes one argument and forward it to the wrapped object : 您可以创建一个包装类,该类包含一个函数,该函数接受一个参数并将其转发给包装对象:

template<typename T>
struct Wrapper {
    template<typename Arg>
    decltype(auto) test(Arg&& arg) {
        return t.test(std::forward<Arg>(arg));
    }

    T t;
};

Notice the use of perfect forwarding here without any variadic templates. 注意在没有任何可变参数模板的情况下使用完美转发。 If t.test would require a move only type as parameter, it would not be possible to call it without the forward<Arg>(arg) . 如果t.test需要仅移动类型作为参数,则不能在没有forward<Arg>(arg)情况下调用它。


The second thing happening here is the parameter not being followed by && . 这里发生的第二件事是参数没有被&&跟随。 Adding && to ArgTypes would be a mistake and would make some cases fail to compile. 添加&&ArgTypes将是一个错误,并且会使某些情况无法编译。 Consider this simple case : 考虑这个简单的情况:

std::function<void(int)> f;
int i = 0;
f(i);

That would fail to compile. 这将无法编译。 If you add && to ArgTypes , every parameters that are not reference (eg. int ) would become an rvalue reference on the call operator (in our case, int&& ). 如果你向ArgTypes添加&&ArgTypes每个不引用的参数(例如int )都将成为调用操作符的右值引用(在我们的例子中是int&& )。 Since all parameter types are already qualified correctly in the std::function argument list, what you want to recieve in the call operator is exactly those types, not transformed. 由于所有参数类型都已在std::function参数列表中正确限定,因此您希望在调用运算符中接收的内容正是那些未转换的类型。

The why you need std::forward if you don't use && ? 如果你不使用&& ,为什么你需要std::forward Because even though you don't need to infer value categories, you still need to not copy every arguments to the contained function. 因为即使您不需要推断值类别,仍然需要不将每个参数复制到包含的函数。 If one of the std::function 's parameter is int& , you don't want to move it. 如果std::function的参数之一是int& ,则不想移动它。 But if one of the parameter is std::unique_ptr<int> , you must move it! 但是如果其中一个参数是std::unique_ptr<int> ,你必须移动它! And this is exactly what std::forward is for. 这正是std::forward的用途。 Moving only what should be moved. 只移动应该移动的东西。

std::forward只是将rvalue引用附加到类型,因此考虑引用折叠规则,它有效地传递引用参数as-is并移动对象参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM