Универсальные ссылки и локальные классы

В моем коде ниже у меня есть функция, которая принимает «универсальную ссылку» (F&&). Функция также имеет внутренний класс, который принимает объект F&& в своем конструкторе. Является F&& все еще универсальная ссылка на тот момент? То есть является 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();
}

Живая демоверсия.

3

Решение

Является F&& все еще универсальная ссылка на тот момент? То есть является F все еще считается выведенным типом?

Такого рода путаница — вот почему мне не нравится термин универсальная ссылка … нет такого понятия.

Я предпочитаю понимать код в терминах ссылок lvalue и ссылок rvalue, а также правил свертывания ссылок и вывода аргументов шаблона.

Когда функция вызывается с lvalue типа L Аргумент F будет выведено как L&и по ссылке рушатся правила F&& просто L&, в Task конструктор ничего не меняет, F&& все еще L& поэтому конструктор принимает ссылку на lvalue, которая привязана к lvalue, переданному в Asyncи поэтому вы не хотите перемещать его, и forward подходит, потому что это сохраняет категорию значения, пересылая lvalue как lvalue. (Переход от lvalue удивит вызывающего Async, который не ожидал, что lvalue будет тихо перемещен.)

Когда функция вызывается со значением типа r R Аргумент F будет выведено как R, так что F&& является R&&, в Task конструктор ничего не меняет, F&& все еще R&& поэтому конструктор принимает ссылку на rvalue, которая привязана к rvalue, переданному в Asyncи так ты мог переместить его, но forward также целесообразно, потому что это сохраняет категорию значения, перенаправляя значение rvalue как значение rvalue.

На прошлой неделе на CppCon Херб Саттер объявил, что предпочтительный термин для «универсальной ссылки» теперь экспедиционная ссылка потому что это лучше описывает, для чего они используются.

6

Другие решения

Ктор не является универсальная ссылка, но стандартная ссылка rvalue или lvalue-ссылка. Проблема с вашей конструкцией в том, что вы понятия не имеете, просто она отражает Async (что может быть достаточно)!

Чтобы быть универсальной ссылкой, тип должен быть выведен за этот звонок, и не раньше, чем-то связанным.

std::forward я все еще уместен там, поскольку аргумент внешних функций действительно должен быть передан созданному объекту с сохраненной семантикой перемещения / копирования.

1

По вопросам рекламы [email protected]