Страницы

Поиск по вопросам

среда, 10 октября 2018 г.

Повсеместное использование r-value ссылок

Стоит ли повсеместно стараться как можно чаще использовать r-value ссылки? Вот, допустим, код:
std::string hi() { return "hello world
" }
auto&& str = hi();
В данном случае в строке 5 происходит лишь одно создание объекта, и r-value ссылка на него, и никакого копирования... эффективно, если сравнить, например, с:
auto str = hi();
или
auto str = std::move(hi());
Посему на первый взгляд напрашивается вывод: почему бы не создавать r-value ссылки как можно чаще вместо обычной инициализации переменной? Особенно если нужно создавать 100000 раз в секунду эти переменные. Допустим, если можно, то писать так:
void function() { int&& a = 1; double&& b = 2.; auto&& value = return_value(); /*далее какой-то код*/ }
и далее работать с этими ссылками или передавать их в другое место... выглядит, это чуть более монстрообразно, чем обычная инициализация переменных, к которой глаз привык:
void function() { int a = 1; double b = 2.; auto value = return_value(); /*далее какой-то код*/ }
Поэтому стоит ли как можно чаще пользоваться r-value ссылками вместо обычной инициализации новых переменных в локальном контексте(внутри функций, например) для эффективности, или есть ли какие-то встроенные оптимизации, которые при int a = 2 делают те же действия, что и int&& a = 2?


Ответ

Обычно ссылки хранятся в памяти в виде указателей на объекты. Поэтому в данном предложении
auto &&a = 1;
будет создан временный объект со значением 1 и ссылка на него.
Если у вас есть объявление вида
auto a = f();
где функция f возвращает объект некоторого типа T, то этот объект, возвращаемый из функции, строится на месте переменной a , минуя вызов копирующего или перемещающего конструктора.
Рассмотрите следующий пример
#include
struct Int { Int( int x = 0 ) : x( x ) { std::cout << "Int::Int( int )" << std::endl; }
Int( const Int &rhs ) : x( rhs.x ) { std::cout << "Int::Int( const Int & )" << std::endl; }
Int( Int &&rhs ) : x( rhs.x ) { std::cout << "Int::Int( Int && )" << std::endl; }
~Int() { std::cout << "Int::~Int()" << std::endl; }
int x; };
Int f( int x ) { return x; }
int main() { auto x = f( 10 ); auto &&y = f( 20 );
return 0; }
Вывод на консоль будет следующим
Int::Int( int ) Int::Int( int ) Int::~Int() Int::~Int()
То есть в обоих случаях будет вызвано по одному конструктору и, соответственно, деструктору.
К тому же нельзя создавать, например, массивы из ссылок. В нижеприведенной программе, если раскомментировать предложение с массивом ссылок, то будет выдано сообщение об ошибке.
#include
struct Int { Int( int x = 0 ) : x( x ) { std::cout << "Int::Int( int )" << std::endl; }
Int( const Int &rhs ) : x( rhs.x ) { std::cout << "Int::Int( const Int & )" << std::endl; }
Int( Int &&rhs ) : x( rhs.x ) { std::cout << "Int::Int( Int && )" << std::endl; }
~Int() { std::cout << "Int::~Int()" << std::endl; }
int x; };
Int f( int x ) { return x; }
int main() { Int a[] = { f( 10 ) }; //Int &&b[] = { f( 10 ) };
return 0; }

Комментариев нет:

Отправить комментарий