#cpp #cpp11
Стоит ли повсеместно стараться как можно чаще использовать r-value ссылки? Вот, допустим, код: std::string hi() { return "hello world\n" } 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?
Ответы
Ответ 1
Обычно ссылки хранятся в памяти в виде указателей на объекты. Поэтому в данном предложении auto &&a = 1; будет создан временный объект со значением 1 и ссылка на него. Если у вас есть объявление вида auto a = f(); где функция f возвращает объект некоторого типа T, то этот объект, возвращаемый из функции, строится на месте переменной a , минуя вызов копирующего или перемещающего конструктора. Рассмотрите следующий пример #includestruct 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; } Ответ 2
На самом деле доступ к переменной напрямую всегда быстрее чем по ссылке. Составим простую программу: int O() { int x = 5; return x; } int test(){ auto&& value = O(); return value; } int test2(){ auto value = O(); return value; } Соберём БЕЗ оптимизаций. test(): push rbp mov rbp, rsp sub rsp, 16 call O() mov DWORD PTR [rbp-12], eax lea rax, [rbp-12] mov QWORD PTR [rbp-8], rax mov rax, QWORD PTR [rbp-8] mov eax, DWORD PTR [rax] leave ret test2(): push rbp mov rbp, rsp sub rsp, 16 call O() mov DWORD PTR [rbp-4], eax mov eax, DWORD PTR [rbp-4] leave ret Как видите работа напрямую - меньше операций сразу, хотя мы переменную и не использовали считай. В общем для примитивов однозначно лучше переменная, для сложных классов - воспользуйтесь профайлером (возможно есть смысл использовать перемещающий конструктор или что-то такое).Ответ 3
В данном случае нет никакой практической разницы между auto str = hi(); и auto&& str = hi(); В обоих случаях copy elision и return value optimization приводят к тому, что результат функции hi будет формироваться (конструироваться) напрямую в финальном объекте. В первом случае - напрямую в str. Во-втором случае - напрямую во временном объекте, к которому будет привязываться str.
Комментариев нет:
Отправить комментарий