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

Захват универсальной ссылкой

При передаче выведенного типа в качестве ссылки на r-значение я получаю универсальную ссылочную функциональность и могу архивировать идеальную переадресацию следующим образом:

template <typename T>
void func(T&& t) {
    other_func(std::forward<T>(t));
}

... из-за того, как происходит вывод T и стандартных правил коллапса ссылки.

Теперь рассмотрим, что other_func принимает объект функции

template <typename T>
void func(T&& t) {
    other_func([](int v) { return t + v; }); // I chose addition for example purposes
}

Теперь очевидно, что это не скомпилируется из-за невозможности захвата t. Мой вопрос: как я могу его захватить, чтобы зафиксированное значение было бы тем, что выводится на T?

Возможно ли это, используя новые общие лямбда-захваты? И если... как?

[t = std::forward<T>(t)] ? 

Я до сих пор не получаю механику новых инициализаторов захвата...

4b9b3361

Ответ 1

Вы можете "захватывать по универсальной ссылке" в С++ 11, так как тип параметра шаблона T доступен для функции лямбда (отвратительный пример живого кода в Coliru):

template <typename T>
void func(T&& t) {
  other_func([&t](int v) {
    return std::forward<T>(t) + v;
  });
}

Ответ 2

Хорошо, попробуй это. К сожалению, у меня нет компилятора, который поддерживает эту функцию под рукой, поэтому простите меня, если я серьезно неверно истолковал вещи на этом пути.

Предложение, касающееся этого, N3648.

Интересная часть здесь заключается в том, что тип переменной в захвате init выводится так, как если бы использовалось auto:

Тип этого элемента соответствует типу гипотетического объявление переменной формы "auto init-capture"; [...].

Итак, вопрос о том, что вы получаете из списка захвата [c = std::forward<T>(t)], эквивалентен тому, что вы получаете из объявления auto c = std::forward<T>(t).

Выведенный тип здесь будет std::remove_reference<T>::type (ссылочные квалификаторы отбрасываются на auto), поэтому вы всегда будете иметь здесь новое значение. Если t была ссылкой на rvalue, вы будете перемещать-построить это новое значение, иначе вы будете копировать-построить (из-за возвращаемого значения std::forward).

Хорошо, что эта новая ценность принадлежит лямбда. Итак, вне зависимости от того, какой t вы перенесли вначале, безопасно std::move из захваченного c. Поэтому, даже если вы не знаете тип начального t, вы все равно ничего не потеряли на этом пути.

Ответ 3

Для того, чтобы достичь желаемого поведения при захвате в качестве ссылки, не общей лямбды захвата С++ 14 не требуется (но как всегда с захватом ссылкой, следует соблюдать осторожность, чтобы не создавать оборванные ссылки):

template <typename T>
void func(T&& t) {
    other_func([&t](int v) { return  std::forward<T>(t) + v; });
}

Напротив, если решение принято, чтобы использовать захват значения лямбда должна быть помечена как изменяемые, чтобы обеспечить эффективное перемещение (поскольку константный классификатор неявно добавляется к лямбде):

template <typename T>
void func(T&& t) {
    other_func([t = std::forward<T>(t)](int v) mutable { return  std::move(t) + v; });
}