3

在下面的代码中,我有一个接受“通用引用”(F&&)的函数。该函数还有一个内部类,它F&&在其构造函数中接受一个对象。F&&那时仍然是通用参考吗?ieF仍然被认为是推导类型?

换句话说,我应该在构造函数初始化列表中使用std::forward<F>还是?std::move

#include "tbb/task.h"
#include <iostream>
#include <future>

template<class F>
auto Async(F&& f) -> std::future<decltype(f())>
{
    typedef decltype(f()) result_type;

    struct Task : tbb::task
    {
        Task(F&& f) : f_(std::forward<F>(f)) {} // is forward correct here?

        virtual tbb::task* execute()
        {
            f_();
            return nullptr;
        }

        std::packaged_task<result_type()> f_;
    };

    auto task = new (tbb::task::allocate_root()) Task(std::forward<F>(f));
    tbb::task::enqueue(*task);
    return task->f_.get_future();
}


int main()
{
    Async([]{ std::cout << "Hi" << std::endl; }).get();
}

现场演示。

4

2 回答 2

6

F&&那时仍然是通用参考吗?ieF仍然被认为是推导类型?

这种混乱是我不喜欢通用引用这个词的原因......没有这样的事情

我更喜欢从左值引用和右值引用的角度来理解代码,以及引用折叠和模板参数推导的规则。

当使用类型的左值调用函数时L,参数F将被推断为L&,并且通过引用折叠规则F&&L&。在Task构造函数中没有任何变化,F&&仍然L&是构造函数采用绑定到传递给的左值的左值引用,Async因此您不想移动它,并且forward是合适的,因为它保留了值类别,将左值作为左值。(从左值移动会让 的调用者感到惊讶Async,他们不会期望左值被默默移动。)

当使用类型的右值调用函数时R,参数F将被推断为R,因此F&&也是如此R&&。在Task构造函数中没有任何变化,F&&仍然R&&是构造函数采用绑定到传递给的右值的右值引用,Async因此您可以移动它,但forward也是合适的,因为它保留了值类别,将右值作为右值转发。

在上周的 CppCon 上,Herb Sutter 宣布“通用参考”的首选术语现在是转发参考,因为这更好地描述了它们的用途。

于 2014-09-18T10:10:53.673 回答
1

ctor不是通用引用,而是沼泽标准右值引用或左值引用。你的结构的问题是你不知道哪个,只是它反映了Async(这可能就足够了)!

为了成为通用引用,必须为那个 call推断出类型,而不是提前某个时间来推断一个有点相关的调用。

std::forward我仍然适合那里,因为外部函数参数确实应该通过保留的移动/复制语义传递给创建的对象。

于 2014-09-18T09:44:56.463 回答