【问题标题】:Universal references and local classes通用引用和本地类
【发布时间】:2014-09-18 09:35:59
【问题描述】:

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

也就是说,我应该在构造函数初始化列表中使用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();
}

Live demo.

【问题讨论】:

标签: c++ forward forwarding perfect-forwarding forwarding-reference


【解决方案1】:

当时F&amp;&amp; 仍然是通用引用吗? IE。 F 仍然被认为是推导类型吗?

这种混淆是我不喜欢通用引用这个词的原因...there's no such thing

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

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

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

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

【讨论】:

  • 感谢您的解释。意识到 std::forward 仅仅意味着保留值类别带来了很多清晰。
【解决方案2】:

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

为了成为通用引用,必须为该调用推断出类型,而不是提前某个时间推断出某种相关的调用。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-20
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    相关资源
    最近更新 更多