我应该啥时候 std::forward 函数调用?

Posted

技术标签:

【中文标题】我应该啥时候 std::forward 函数调用?【英文标题】:When should I std::forward a function call?我应该什么时候 std::forward 函数调用? 【发布时间】:2015-09-24 01:00:11 【问题描述】:

我在 Effective Modern C++ 中看到的代码 sn-p 巧妙地实现了 instrumentation rationale 以创建 函数计时器

auto timeFuncInvocation = 
    [](auto&& func, auto&&... params)
    
        start timer; 
        std::forward<decltype(func)>(func)(
            std::forward<decltype(params)>(params)...); 
        stop timer and record elapsed time; 
    ;

我的问题是关于std::forward&lt;decltype(func)&gt;(func)(...

据我了解,我们实际上是将函数强制转换为其原始类型,但是为什么需要这样做?看起来很简单打电话就可以了。 还有其他情况我们使用完美转发来进行函数调用吗?

这看起来像是 the use of familiar template syntax in lambda expressions 的一个很好的用例,以防我们想让计时器类型成为编译时间常数。

【问题讨论】:

我认为这可能是因为func 可以使用非常量运算符()来实现大函数对象。这样,您可以避免无意义的复制,并允许函数对象的突变 表达式的 type 不会随强制转换而改变,但它的 value category 会。这就是全部而且只有point of forward 目的是确保选择正确的ref-qualified 重载operator() func 的类型,一旦在函数中,就不再是 r 值引用了(假设它是在传入时)。如果是这样,每次你将它传递给一个函数时,它都会尝试移动它!这就是需要std::forward 的原因。 @Cameron 这里没有任何东西被传递给另一个函数。我的问题是针对这种特定情况的。 Tavian Barnes 指出的原因(并由 Kerrek 暗示)似乎令人信服 【参考方案1】:

更好地描述 std::forward&lt;decltype(func)&gt;(func)(...) 所做的事情是保留传递给 lambda 的参数的值类别

考虑以下具有 ref 限定 operator() 重载的函子。

struct foo

    void operator()() const &&
     std::cout << __PRETTY_FUNCTION__ << '\n'; 

    void operator()() const &
     std::cout << __PRETTY_FUNCTION__ << '\n'; 
;

请记住,在 lambda func 的主体内是一个左值 (because it has a name)。如果您没有forward 函数参数,则永远无法调用&amp;&amp; 限定的重载。此外,如果没有 &amp; 限定的重载,那么即使调用者向您传递了一个右值 foo 实例,您的代码也将无法编译。

Live demo

【讨论】:

您同意decltype(func) 部分仅在没有为func 拼写类型信息的通用lambda 中需要吗?如果是这种情况,我可以为template &lt;class F&gt; void Foo(F&amp;&amp; func)std::forward&lt;F&gt;(func)(...) 并做同样的事情对吗? @NikosAthanasiou 是的,那会做同样的事情 直到我意识到func 不仅可以是函数指针、lambda 或std::function 实例,它还可以是任何声明@ 的类的实例时,我才明白为什么这很重要987654337@。只有这样它才变得重要。

以上是关于我应该啥时候 std::forward 函数调用?的主要内容,如果未能解决你的问题,请参考以下文章

普通的右值引用和 std::forward 返回的有啥区别?

理解std::move和std::forward

在 c 语言中调用函数时,啥时候应该添加“&”,啥时候不应该添加?

SGI slist 和 C++11 forward_list 有啥区别?

将 std::forward_as_tuple() 结果传递给可能从该对象的右值引用成员移动的多个函数?

std::move和std::forward