Аргумент шаблона rvalue неявно используется как lvalue, и работает std::forwarding

Этот пример использования std::forward меня озадачивает. Это моя отредактированная версия:

#include <iostream>
#include <memory>
#include <utility>
using namespace std;

struct A{
    A(int&& n) { cout << "rvalue overload, n=" << n << "\n"; }
    A(int& n)  { cout << "lvalue overload, n=" << n << "\n"; }
};

template<typename> void template_type_dumper();

template<class T, class U>
unique_ptr<T> make_unique(U&& u){
    //Have a "fingerprint" of what function is being called
    static int dummyvar;
    cout<<"address of make_unique::dummyvar: "<<&dummyvar<<endl;
    //g++ dumps two warnings here, which reveal what exact type is passed as template parameter
    template_type_dumper<decltype(u)>;
    template_type_dumper<U>;

    return unique_ptr<T>(new T(forward<U>(u)));
}

int main()
{
    unique_ptr<A> p1 = make_unique<A>(2); // rvalue
    int i = 1;
    unique_ptr<A> p2 = make_unique<A>(i); // lvalue
}

Выход

address of make_unique::dummyvar: 0x6021a4
rvalue overload, n=2
address of make_unique::dummyvar: 0x6021a8
lvalue overload, n=1

а предупреждения о ссылке на template_type_dumper показывают, что в первом экземпляре decltype(u) = int&& и U = int, во втором decltype(u) = int& и U = int&.

Очевидно, что есть два разных экземпляра, как и ожидалось, но это мои вопросы:

  1. как std::forward может здесь работать? В первом экземпляре его аргумент шаблона явно равен U = int, откуда ему знать, что он должен вернуть ссылку на rvalue? Что произойдет, если вместо этого я укажу U&&?
  2. make_unique объявляется принимающим ссылку на значение rvalue. Почему u может быть ссылкой на lvalue? Есть ли какое-то особое правило, которое я упускаю?

person Lorenzo Pistone    schedule 30.04.2012    source источник
comment
Вам не хватает свертывания ссылок.   -  person ildjarn    schedule 01.05.2012


Ответы (1)


make_unique объявляется принимающим ссылку на значение rvalue. Как получилось, что вы можете быть ссылкой на lvalue? Есть ли какое-то особое правило, которое я упускаю?

make_unique объявляется принимающим ссылку. Что это за ссылка, предстоит выяснить. Если передается lvalue типа foo, U выводится как foo&, а U&& становится foo& из-за правил сворачивания ссылок (по сути, «объединение» ссылки lvalue с другой ссылкой всегда дает ссылку lvalue; объединение двух ссылок rvalue дает ссылку rvalue ). Если передается rvalue типа foo, U выводится как foo, а U&& равно foo&&.

Это одна из вещей, которая обеспечивает идеальную пересылку: с U&& вы можете принимать как lvalue, так и rvalue, а U выводится для соответствия соответствующей категории значений. Затем с помощью std::forward вы можете пересылать значения, сохраняя ту же категорию значений: в первом случае вы получаете std::forward<foo&>, которое пересылает lvalue, а во втором случае вы получаете std::forward<foo>, которое пересылает rvalue.

В первом экземпляре его аргумент шаблона явно U = int, как он может знать, что он должен вернуть ссылку на rvalue?

Поскольку возвращаемый тип std::forward<T> всегда T&&. Если вы передаете int, возвращается int&&. Если вы передаете int&, он снова возвращает int& из-за правил свертывания ссылок.

Что произойдет, если я укажу U&& вместо этого?

У вас будет std::forward<int&&>, а правила свертывания ссылок сделают int&& && ссылкой на rvalue по-прежнему: int&&.

person R. Martinho Fernandes    schedule 30.04.2012
comment
Жаль, что большинство онлайн-статей, кажется, больше сосредоточены на семантике перемещения при объяснении rvalue-ссылок, а не на этом механизме свертывания ссылок, который в основном отвечает на все мои вопросы. - person Lorenzo Pistone; 01.05.2012
comment
U — это тип, поэтому на самом деле у него нет категории значений. Скорее, U выводится либо как не ссылочный, либо как ссылочный тип lvalue. - person Kerrek SB; 01.05.2012
comment
@KerrekSB Я исправил формулировку :) - person R. Martinho Fernandes; 01.05.2012
comment
@LorenzoPistone: семантика перемещения наиболее полезна. Свертывание ссылок и т. д. описать намного сложнее, и они полезны только в том случае, если вы пишете функцию пересылки шаблонов. Что делают далеко не все. - person Nicol Bolas; 01.05.2012