Нужно распасться в идеальной пересылке - PullRequest
0 голосов
/ 27 августа 2018

Я не понимаю, почему следующий код недействителен.

#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 одинаков.

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Если вы используете decay и передаете значение r в значение funkb, тогда decay ничего не делает, так как тип уже "распался" (т.е. это T).

Однако, если вы передадите lvalue, тип ссылки на переадресацию будет T&, а когда вы ее потеряете, вы получите T. Это означает, что результат std::forward будет rvalue в обоих случаях.

Теперь код недействителен без затухания, потому что funka принимает ссылку на rvalue, и вы передаете lvalue в funkb, который затем пересылаете (сохраняя категорию значений). Вы в основном делаете

int a;
int&& b = a;

Как вы можете видеть, при распаде вы всегда получите rvalue, который может связываться со ссылкой на rvalue.

0 голосов
/ 27 августа 2018

Почему это недействительный код?

В вызове funkb( tup ), tup - это lvalue . Из-за правил перенаправления ссылки , тип аргумента v в funka выводится как std::tuple<int, int>&.

std::forward<std::tuple<int, int>&>( v ) не двигается v - это все еще значение .

Lvalue s не привязываются к rvalue ссылкам .


Он компилируется, если я перехожу с затуханием.

std::decay_t удаляет все cv-квалификаторы и ссылки. Это меняет ваш forward вызов на что-то вроде: std::forward<std::tuple<int, int>>( v ).

Когда std::forward вызывается без ссылки или rvalue ссылка в качестве параметра шаблона, он будет перемещаться v.

...