Думаю все уже читали вот
это? Краткий смысл в том, что когда мы передаем параметр по значению в функцию, корорая должна сделать копию параметра, компилятор получает возможность соптимизировать конструктор копирования.
Т.е. сравним две фунции:
void foo1(string par)
{
// modify par...
}
void foo2(string const & par)
{
string cpy(par);
// modify cpy...
}
То foo1 будет более оптимальна, чем foo2.
Однако в C++11 у нас есть возможность использовать &&, и мы перегрузим foo2 следующим образом:
void foo2(string && par)
{
// modify par...
}
Теперь foo2, перегруженная для const& и &&, будет немного более оптимальна, чем foo1. Но с другой стороны, если количество параметров, для которых нужно иметь подобные перегрузки, больше чем один, то количество перегрузок растет как 2^N. Зададимся таким вопросом: насколько имеет смысл делать несколько перегрузок с передачей параметров по ссылке вместо простой передачи параметров по значению?
Насколько вообще перегрузки для const& и && ссылок эффективнее передачи по значению?
Здесь надо рассмотреть три варианта: когда в функцию передается lvalue, xvalue и prvalue. prvalue — это pure rvalue, и xvalue — это expiring value. (Этот анализ я видел на clc++.moderated, но сейчас его найти мне не удалось.)
Для lvalue здесь все просто:
string lv{"abc"};
foo1(lv); // copy conductor for foo1 par
foo2(lv); // copy conductor for cpy inside foo2
prvalue:
foo1(string{"abc"}); // nothing (copy/move constructor is elided)
foo2(string{"abc"}); // nothing (foo2(&&))
xvalue:
string xv1{"abc"};
foo1(std::move(xv1)); // move constructor for foo1 par
string xv2{"abc"};
foo2(std::move(xv2)); // nothing (foo2(&&))
Таким образом видно, что для функции с параметром по значению происходит дополнительный вызов move конструктора, когда мы явно передаем && ссылку. В остальных случаях foo1 и foo2 дают один и тот-же эффект. Если для нашего типа операция move constructor достаточно дешевая, то разницей в эффективности функций foo1 и foo2 можно пренебречь.
Когда это может быть полезно? Если подумать перегрузки функции для const & и && случаются не так уж и редко. Например, вот эта всем знакомая функция:
Obj & operator=(Obj const & other) { … }
Obj & operator=(Obj && other) { … }
Два оператора присваивания можно заменить на один:
Obj & operator=(Obj other)
{
// with typical implementation:
other.swap(*this);
return *this;
}
Или вот пример (немного модифицированный), который часто встречается в презентациях Страуструпа, когда он рассказывает о rvalue ссылках:
Matrix operator+(Matrix const & m1, Matrix const & m2) { … }
Matrix operator+(Matrix && m1, Matrix const & m2) { … }
// single overload, practically equivalent in performance:
Matrix operator+(Matrix m1, Matrix const & m2) { … }
Я даже рискну сделать следующий неожиданный вывод: всегда, когда есть необходимость иметь две перегрузки функции для const & и && параметров, их можно заменить на одну с передачей параметров по значению. Т.е. с практической точки зрения в большинстве случаев передавать параметры по rvalue ссылке вообще не нужно.