2016-11-23 4 views
2

У меня есть шаг-только STRUCT Foo и функция Foo get();Есть ли какая-нибудь причина для захвата возвращаемого значения как rvalue-reference?

Если я хочу, чтобы захватить возвращаемое значение get в изменчивой манере, у меня есть два варианта:

  • по значению (Foo)
  • по RValue ссылка (Foo&&)

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

Я изо всех сил пытаюсь понять точку между различными вариантами?

  • Есть любой разница между этими двумя?

Рабочий пример:

#include <iostream> 

struct Foo 
{ 
    Foo(std::string s) : s(std::move(s)) {} 
    Foo(Foo&& f)  : s(std::move(f.s)) {} 

    Foo(const Foo&) = delete; 
    Foo& operator=(const Foo&) = delete; 

    std::string s; 
}; 

Foo get() 
{ 
    return Foo { "hello" }; 
} 

int main() 
{ 
    // capture return value as l-value 
    Foo lv1 = get(); 

    // move into another lvalue 
    Foo lv2 = std::move(lv1); 
    std::cout << lv2.s << '\n'; 

    // capture return value as r-value reference 
    Foo&& rv1 = get(); 

    // move into another lvalue 
    Foo lv3 = std::move(rv1); 
    std::cout << lv3.s << '\n'; 

    return 0; 
} 

ответ

3
Foo lv1 = get(); 

Это требует, чтобы Foo является копией/подвижны.

Foo&& rv1 = get(); 

Это не (по крайней мере, не так далеко, как эта строка кода касается, реализация get все еще может потребоваться один).

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

Так что если вы хотите наложить как можно меньше ограничений на тип Foo, вы можете сохранить && возвращаемого значения.

Конечно, C++ 17 изменяет это правило так, чтобы первому не нужен конструктор copy/move.

2
Foo&& 

Это создает ссылку на временное значение (или хранит присвоенную ему ссылку rvalue). Если он хранит ссылку на временное значение, его время жизни увеличивает значение.

Foo 

В этом случае копия значения хранится независимо от того, что было возвращено.

В C++ 11 и 14, если Foo не может быть перемещен, присвоение Foo make_foo() переменной Foo является незаконным. Там есть перемещение, даже если перемещение отменяется (и возвращаемое значение и значение во внешнем пространстве сливают сроки жизни).

В C++ 17 гарантированное исключение означает, что конструктор перемещения не существует.

Foo x = make_foo(); // Foo make_foo() 

выше в C++ 17 гарантирует, что возвращаемое значение make_foo() только по имени x. Фактически, временный в пределахmake_foo также может быть x; тот же объект с разными именами. Никакого перемещения не требуется.

Есть еще несколько тонких отличий. decltype(x) вернет объявленный тип x; поэтому Foo или Foo&& в зависимости.

Другим важным отличием является его использование с auto.

auto&& x = some_function(); 

Это создает ссылку на что угодно. Если some_function возвращает временное, он привязывает к нему ссылку rvalue и продлевает срок ее службы. Если он возвращает ссылку, x соответствует типу ссылки.

auto x = some_function(); 

это создает значение, которое может быть скопировано из каких some_function возвращается, или может быть опущено с возвращаемым значением some_function, если она возвращает временную.

auto&& означает, в некотором смысле, «просто заставить его работать и не выполнять лишнюю работу», и он может выводить на Foo&&. auto означает «хранить копию».

В стиле «почти всегда авто» они будут более распространены, чем явные Foo или Foo&&.

auto&& никогда не будет выведено до Foo, но может быть получено до Foo&&.

Наиболее распространенные виды использования auto&&, даже за пределами почти всегда авто, будет:

for(auto&& x : range) 

где x становится эффективным способом перебора диапазона, где мы не волнует, какой тип имеет range много. Другим распространенным является использование:

[](auto&& x){ /* some code */ } 

лямбды часто используются в ситуациях, когда тип очевиден и не стоит вводить снова, как передается алгоритмам или тому подобное. Используя auto&& для типов параметров, мы делаем код менее подробным.

+0

«Если он хранит ссылку на временное значение, его время жизни увеличивает значение». - только если возвращаемое значение является значением prvalue. Ловушка была бы, если функция возвращает значение x, обозначающее временное, например. 'Foo && f = std :: move (std :: string {});' –

+0

@ м.м. да, это трудно сказать правильно, не переходя в стандартную – Yakk