2017-03-05 68 views
2

我一直在寻找在std::function执行和它的呼叫operator()在std :: function运算符()和std :: forward中发生了什么?

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的意图是什么?我很困惑 :-)?

+3

'*它不是*'不是吗?当然看起来对我来说。 – Biffen

+0

该声明是std :: function和templ的完全专业化。参数'Ret,ArgTypes'已经修复 – Gabriel

+3

看起来像一个类模板的部分专业化的成员函数给我。我不确定你在哪里看到完整的专业化(完全专业化会有空的模板参数列表)。 – 2017-03-05 15:51:47

回答

3

你是对的,这不是你典型的“完美转发”场景。一个简单的例子可以帮助说明动机。假设类型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) 

说明:

a1a2构造堆栈上。然后,当传入function调用者时,首先复制a1以绑定到第一个按值参数,然后在a1上调用std::forward<A>,其中从by-value参数移动到lambda中。

相反,a2不需要被复制到绑定到functionA&参数,然后std::forward<A&>(a2)被调用,它转发a2作为左值代替右值,并且该结合拉姆达的A&参数。

然后事情被破坏。 ~A(-1)表示AA的移动构建状态下销毁。

总之,即使ArgTypes未按照通常的完美转发方式推导出来,我们仍然希望将值ArgTypes转换为右值,并将引用ArgTypes作为左值。所以std::forward只是恰好做到了我们在这里想要的。

+0

@加布里埃尔:对不起,我不明白后续问题。你可以再详细一点吗? –

+0

@Gabriel:'std :: function' **是按照我上面描述的**制定的。如果这就是你的问题中的“那个”,那么标准已经是这样了。对不起,如果我很密集。目前我已经睡了很多小时了。 –

0

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

1

我觉得你很困惑这里很多事情。

首先,完美转发与可变参数模板无关。你可以创建一个具有功能,它有一个参数,并将它转发给被包装的对象的包装类:

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的目的。只移动应该移动的内容。

相关问题