Правила свертывания ссылок не применяются должным образом?

Я освежаю свою память о том, как совершенная переадресация работает в C++. Я понимаю, что вызов std::forward вынужден предоставлять явный параметр шаблона по какой-то причине (т.е. при работе со ссылками rvalue, которые на самом деле являются lvalues), однако при проверке работоспособности фактического кода я был удивлен этим (несколько связанным) сценарий:

#include <iostream>
#include <utility>
#include <type_traits>

template<class T>
T&& fwd(T& t) {
    return static_cast<T&&>(t);
}

template<class T>
T&& fwd(T&& t) {
    return static_cast<T&&>(t);
}

int main()
{
    int lnum = 3;
    if (std::is_rvalue_reference<decltype(fwd(lnum))>::value)
        std::cout << "It's rref." << std::endl;           // this get's printed on screen 
    else
        std::cout << "It's lref." << std::endl;

    return 0;
}

Если я правильно понимаю свертывание ссылок (а я думаю, что понимаю), вывод типа должен быть таким:

int& && fwd(int& & t) {                        
    return static_cast<int& &&>(t);         
}

ведущий к

int& fwd(int& t) {                        
    return static_cast<int&>(t);         
}

Ясно, что это не так. Что мне здесь не хватает?


person mdx    schedule 28.10.2020    source источник
comment
В вашем случае вызывается fwd(T& t), поэтому T это int, и в результате у вас есть int&&. fwd(T&) лучше подходит, чем fwd(T&&). Так что здесь нет места для ссылок.   -  person rafix07    schedule 28.10.2020


Ответы (2)


На самом деле никакого свертывания ссылок не происходит. Соответствующий шаблон функции, на который следует обратить внимание, т. е. тот, который выбран:

template<class T>
T&& fwd(T& t) { // <-- not a forwarding reference
    return static_cast<T&&>(t);
}

Обратите внимание, что в этом шаблоне функции нет пересылающих ссылок — параметр функции t является просто ссылкой lvalue (T& t).

Параметр шаблона T выводится как int, а не как int&, потому что t — это не ссылка для пересылки, а просто ссылка на lvalue. Если вы просто замените T на int в приведенном выше шаблоне функции, вы получите:

template<class T>
int&& fwd(int& t) {
    return static_cast<int&&>(t);
}

Свертывание ссылок не применяется, так как здесь нет ничего, что в противном случае могло бы стать ссылкой на ссылку (например, int& && или int&& &&).

person 眠りネロク    schedule 28.10.2020
comment
По какой-то причине я экстраполировал свертывание ссылок из-за переадресации ссылок на все ссылки. Чувствует себя невероятно глупо. Спасибо за поправку :) - person mdx; 28.10.2020
comment
@TuRtoise свертывание ссылки применяется, когда выведенный код в противном случае привел бы к ссылке на ссылку, которая не может существовать в C++. Однако это не зависит от того, используете ли вы переадресацию ссылок или нет. Например, если вы вызываете fwd() как fwd<int&>(lnum), то T будет int&, и произойдет свертывание ссылки: возвращаемый тип int& && свернется в int&. Вы можете проверить, что возвращаемый тип является ссылкой lvalue, потому что присваивание fwd<int&>(lnum) = 0; действительно компилируется. Ни fwd<int>(lnum) = 0;, ни fwd<int&&>(lnum) = 0; не компилируются (ссылка rvalue). - person 眠りネロク; 28.10.2020
comment
@mdx Я думаю, что вы, возможно, экстраполировали правила вывода типов для пересылки ссылки на ссылки lvalue, но не свертывания ссылок. Я надеюсь, что теперь это более ясно. - person 眠りネロク; 28.10.2020
comment
Получается, я не правильно понял тему. Я рад, что спросил. Еще раз большое спасибо. - person mdx; 29.10.2020

Во-первых, вызывается функция T&& fwd(T& t). Таким образом, параметр ссылки на пересылку отсутствует. Параметр является ссылкой lvalue, а выведенное T равно int. Таким образом, нет ссылок на свертывание, а статическое приведение дает int&&.

Если бы вызываемая функция была T&& fwd(T&& t) (т. е. если бы не существовало более подходящей перегрузки), то ваше объяснение свертывания ссылок было бы правильным (за исключением параметра, который был бы int& &&, который также сворачивает int&), и тип возвращаемого значения действительно был бы ссылка на lvalue.

person eerorika    schedule 28.10.2020
comment
Отличный ответ. Однако мне нравится тот, который я принял, немного больше, потому что я переварил его немного быстрее. Тем не менее, спасибо. - person mdx; 28.10.2020