Я провел несколько часов о Rvalue s и lvalue. Вот что я понимаю
int main()
{
//.....
Foo foo = Bar1();
foo = Bar2();
//......
}
Foo Bar1()
{
//Do something including create foo
return foo;
}
Foo& Bar2()
{
//Do something including create foo
return foo;
}
Под с ++ 03, Bar1()
скопирует возвращаемый объект (непосредственно перед возвратом), а затем вернет адрес скопированного объекта; выполнение расточительной копии объекта, который собирается быть уничтоженным. Bar2()
вернет объект, созданный в функции.
Под с ++ 11, Bar1()
а также Bar2()
будет по существу эквивалентным (а также эквивалентным Bar2()
с ++ 03).
Это правильно? Если нет, пожалуйста, уточните.
Понятие rvalues и lvalues не изменилось с более старых C ++ на C ++ 11. То, что вы описываете как «C ++ 03», это то, что должно произойти. Некоторые оптимизации компилятора в некоторых случаях могут уменьшить количество ненужных копий (включая ненужные вызовы конструктора копирования!), Но в остальном это то же самое.
Что изменилось, так это то, что в C ++ 11 появилась концепция rvalue-reference (T&&
).
На нем есть несколько статей, которые вы можете найти в Google, например здесь:
http://thbecker.net/articles/rvalue_references/section_01.html
Они не то же самое. Bar2()
это UB по обоим стандартам. Вы не можете вернуть объект, созданный в стеке, по ссылке.
В С ++ 03 Bar1()
может воспользоваться RVO и ничего не будет скопировано. В С ++ 11 Bar1()
будет даже использовать RVO или будет использовать конструктор перемещения, если RVO невозможен.
Bar2()
не создает копии ни в C ++ 2003, ни в C ++ 2011. Для Bar1()
копия foo
создается как в C ++ 2003, так и в C ++ 2011. Использование ссылок на rvalue применяется только в том случае, если у вас действительно есть значение rvalue или если у вас есть значение lvalue, которое собирается исчезнуть и оно возвращается.
Конечно, случаем является неопределенное поведение, потому что foo
возвращение является foo
инициализируется. То есть, кажется, ваш пример испорчен, не указав, что foo
подразумевается, когда он возвращается. Предполагая, что каждая функция имеет локальную переменную foo
, Bar2()
неопределенное поведение в соответствии с обоими стандартами и Bar1()
несколько отличается:
Foo
, C ++ 2011 может использовать конструктор перемещения, в то время как C ++ 2003 может использовать конструктор копирования.return
заявления в Bar1()
вернуть foo
Строительство дополнительного объекта будет выполняться большинством компиляторов.Bar2 () вернет объект, созданный в функции.
Это явно неправильно. Bar2()
вернется ссылка к какому-то объекту. (Примечание: это будет UB, если объект создается в стеке внутри Bar2()
).
В соответствии с c ++ 11 Bar1 () и Bar2 () по существу будут эквивалентны (а также эквивалентны Bar2 () из c ++ 03).
Под C ++ 11 смысл был бы таким же. Что вас действительно интересует, так это семантика перемещения:
Foo Bar3()
{
//Do something
return std::move(foo);
}
Это не будет выполнять конструктор копирования, а конструктор перемещения — который должен быть гораздо менее требовательным к ресурсам.
Эта статья может быть интересна для вас: Хотите скорость? Передать по значению.
В C ++ 03 Bar1()
может скопировать объект (но это оптимизировано с так называемым разрешением копирования — см. ссылку).
В C ++ 11 по сути ничего не изменилось. Компилятору разрешено либо вызывать Foo
Копировать конструктор или Foo
Двигайся конструктор или сделай копию elision. Но поскольку даже в C ++ 03 копия будет удалена, Bar1()
делает то же самое в C ++ 11 и C ++ 03.
Bar2()
верните просто ссылку, в C ++ 11 нет ничего другого. Возвращение ссылок может быть критичным. Если foo
будет локальным значением, ссылка на уже уничтоженную переменную возвращается, но не на это.