Подтвердить что ты не робот

Должны ли шаблонные функции принимать аргументы лямбда по значению или по ссылке rvalue?

GCC 4.7 в режиме С++ 11 позволяет мне определить функцию, берущую лямбду двумя разными способами:

// by value
template<class FunctorT>
void foo(FunctorT f) { /* stuff */ }

и

// by r-value reference
template<class FunctorT>
void foo(FunctorT&& f) { /* stuff */ }

Но не:

// by reference
template<class FunctorT>
void foo(FunctorT& f) { /* stuff */ }

Я знаю, что я могу отменить шаблоны функций и просто взять std:: functions, но foo является маленьким и встроенным, и я хотел бы дать компилятору наилучшую возможность встроить вызовы f внутри. Из первых двух, что предпочтительнее для производительности, если я конкретно знаю, что я прохожу lambdas, и почему не разрешено передавать лямбда на последний?

4b9b3361

Ответ 1

FunctorT&& является универсальной ссылкой и может соответствовать всем, а не только rvalues. Это предпочтительный способ передать вещи в шаблонах С++ 11, если только вам не нужны копии, поскольку он позволяет вам использовать совершенную пересылку. Получите доступ к значению с помощью std::forward<FunctorT>(f), который сделает f значение r снова, если оно было раньше, или оставит его как lvalue. Подробнее о здесь о проблеме переадресации и std::forward и читайте здесь для пошагового руководства о том, как std::forward действительно работает. Это также интересно прочитать.

FunctorT& - это просто ссылка на lvalue, и вы не можете привязать временные ряды (результат выражения лямбда) к этому.

Ответ 2

При создании лямбда-функции вы получаете временный объект. Вы не можете привязывать временные ссылки на не-const l-value. Фактически, вы не можете напрямую создать l-значение, ссылающееся на функцию лямбда.

Когда вы объявляете свой шаблон функции с помощью T&&, тип аргумента для функции будет T const&, если вы передадите объект const функции, T&, если вы передадите объект const не-const l-value к нему и T, если вы передадите ему временное. То есть при прохождении временного объявления функции принимает ссылку на r-значение, которая может быть передана без перемещения объекта. При передаче аргумента явно по значению временный объект концептуально копируется или перемещается, хотя эта копия или перемещение обычно исчезают. Если вы только передаете временные объекты своим функциям, первые два объявления будут делать то же самое, хотя первая декларация может ввести перемещение или копию.

Ответ 3

Это хороший вопрос - первая часть: передача по значению или использование пересылки. Я думаю, что вторая часть (имеющая FunctorT& в качестве аргумента) была удовлетворительно ответила.

Мой совет таков: используйте переадресацию, только когда объект функции известен заранее, чтобы изменить значения в его закрытии (или списке захвата). Лучший пример: std:: shuffle. Он принимает унифицированный генератор случайных чисел (функциональный объект), и каждый вызов генератора изменяет его состояние. Объект функции пересылается в алгоритм.

В каждом другом случае вы предпочитаете передавать по значению. Это не мешает вам захватывать локальных жителей по ссылке и изменять их в вашей лямбда-функции. Это будет работать так, как вы думаете. Как говорит Дитмар, не должно быть накладных расходов на копирование. Также будет применена инкрустация, и ссылки могут быть оптимизированы.