Я не понимаю, почему следующий код недействителен.
#include <type_traits>
#include <tuple>
template<typename... Ts>
void funka( std::tuple<Ts...>&& v ) {
}
template<typename T>
void funkb( T&& v ) {
funka( std::forward<T>( v ) );
}
void funk() {
auto tup = std::tuple<int,int>( 1, 2 );
funkb( tup );
}
Это не сработает с этой ошибкой:
<source>: In instantiation of 'void funkb(T&&) [with T = std::tuple<int, int>&]':
<source>:24:16: required from here
<source>:10:10: error: cannot bind rvalue reference of type 'std::tuple<int, int>&&' to lvalue of type 'std::tuple<int, int>'
funka( std::forward<T>( v ) );
~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
Он компилируется, если у меня forward с распадом.
template<typename T>
void funkb( T&& v ) {
funka( std::forward<std::decay_t<T>>( v ) );
}
Так вот вопрос. Почему это неверный код? Мне кажется, что результирующий тип параметра funkb и funka одинаков.
заранее спасибо





Why is that not valid code?
В вызове funkb( tup )tup - это lvalue. Из-за правил вывода ссылка на пересылку тип аргумента v в funka выводится как std::tuple<int, int>&.
std::forward<std::tuple<int, int>&>( v ) не перемещает v - это все еще lvalue.
Lvalue не привязываются к ссылки rvalue.
It compiles if I forward with a decay.
std::decay_t удаляет все cv-квалификаторы и ссылки. Он изменяет ваш вызов forward на что-то вроде: std::forward<std::tuple<int, int>>( v ).
Когда std::forward вызывается с не-ссылкой или ссылка rvalue в качестве параметра шаблона, он перемещает v.
Если вы используете decay и передаете rvalue в funkb, тогда decay ничего не делает, поскольку тип уже «распался» (то есть это T).
Однако, если вы передадите lvalue, тогда тип ссылки пересылки будет T&, и когда вы его распустите, вы получите T. Это означает, что результатом std::forward в обоих случаях будет rvalue.
Теперь код недействителен без распада, потому что funka принимает ссылку rvalue, и вы передаете lvalue в funkb, который затем пересылаете (с сохранением категории значений). Вы в основном делаете
int a;
int&& b = a;
Как видите, при распаде вы всегда будете получать rvalue, которое можно привязать к ссылке rvalue.
&& в funka - это ссылка на r-значение, а аналогичный вид && в funkb - это ссылка на пересылку.