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

Разве бессмысленно объявлять возвращаемый тип функции как T &&?

Как мы знаем, в большинстве распространенных случаев T&& означает "это временный объект". Однако, если кто-то хочет вернуть временный объект из функции, он может объявить функцию следующим образом:

template<class T>
T f()
{
    T t;
    ......

    return t;
}

или (Примечание: Неправильно)

template<class T>
T&& f()
{
    T t;
    ......

    return t;
}

Но я думаю, что последнее слишком много, потому что первое достаточно и обратно совместимо.

Тем не менее, я также обнаружил, что тип возврата std::forward() объявлен как T & &, поэтому я уверен, что мое понимание об этом неполно.

Мой настоящий вопрос: когда и где мы должны объявлять возвращаемый тип функции как T & &?

4b9b3361

Ответ 1

В вашем примере T&& неверно, это болтливая ссылка.

Но std::forward не возвращает ссылку rvalue на локальную переменную в своем собственном определении, она возвращает ссылку rvalue на свой аргумент by-rvalue-reference (или ссылку lvalue на аргумент by-lvalue-reference).

Вы должны вернуть ссылку rvalue только в том случае, если вы хотите, чтобы вызывающая сторона вашей функции могла перемещаться из любой ссылки, ссылающейся на нее.

Обычно это будет только в том случае, если целью этой функции является предоставление доступа к некоторому значимому объекту (возможно, который уже существует). Таким образом, это включает std::move (который позволяет вам перемещаться из lvalue), и аналогичным образом вы можете написать функцию доступа, специально предназначенную для перехода пользователей из элемента данных какого-либо объекта или элемента из какого-либо контейнера. Если сам объект не является значимым, только значение, то вы можете вернуться по значению.

Как говорит гризли, иногда из-за слияния ссылок вы можете воспользоваться трюками, которые означают, что вы вводите T&& в свой код, но когда T уже является ссылочным типом lvalue T&&, является той же ссылкой на значение lvalue тип. std::forward использует этот трюк. То есть, из-за ссылочного коллапса T&& не означает "rvalue reference to T", это означает "T, если T является ссылочным типом, иначе rvalue ссылается на T".

Ответ 2

T&& не обязательно означает, что результатом является r-значение. При использовании с параметром шаблона && обозначает универсальную ссылку, которая может быть либо rvalue, либо ссылкой lvalue. Более конкретно: Если T является ссылочным типом lvalue foo&, T&& на самом деле является значением lvalue для foo, в противном случае это обозначает ссылку rvalue.

Это можно использовать для записи функций, которые принимают любые аргументы:

template<typename T> void foo(T&&);
bar a;
const bar b;
foo(a);//T and T&& will both be bar&
foo(b);//T and T&& will both be const bar&
foo(bar());//T will be bar, T&& will be bar&&

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

Что касается того, когда использовать его как возвращаемый тип: редко, хотя может быть полезно избежать копирования, когда передаются аргументы, например:

template<typename T> T&& foo(T&& bar) {/*some ops*/ return std::forward<T>(bar);}

Ответ 3

Ответ зависит от того, является ли ваша функция функцией шаблона или нет. В вашем вопросе это было, но пусть сначала взгляните, если это не так:

Non-шаблон

T&& не является бессмысленным как возвращаемый тип. Причина в том, что это, по сути, не означает "временный объект". Это означает "rvalue reference". Разница тонкая, но может быть выделена классом функций, для которых этот тип возврата имеет значение. А именно, что, если мы хотим вернуть ссылку на rvalue, но объект, на который он ссылается, не является локальным объектом нашей функции?

T&& return_rvalue(/*some data*/)
{
   T&& t = Get_a_reference();
   // Do something fascinating.
   return static_cast<T&&>(t);
}

Один очень частный случай этого шаблона - это функция std::move, которая принимает любую ссылку и возвращает ей соответствующую ссылку rvalue. Естественно, что в реальном коде вы должны, конечно, использовать std::move, а не выполнять трансляцию напрямую, так как это более четко показывает ваше намерение.

Шаблон

Если T является параметром шаблона, T&& - это то, что Скотт Майерс называют Универсальная ссылка, Это означает, что тип T&& будет вычисляться с использованием свертывания ссылок... Короче говоря, это означает, что T&& является rvalue, если T я не ссылочный тип, а ссылка lvalue, если она есть.