Если вы думаете о пользовательском строковом классе с поддержкой перемещения, правильный способ использования каждой комбинации категорий значений аргумента:
S operator+(S const& lhs, S const& rhs);
S operator+(S && lhs, S const& rhs);
S operator+(S const& lhs, S && rhs);
S operator+(S && lhs, S && rhs);
Функции возвращают prvalue вместо xvalue . Возвращение xvalues обычно очень опасная вещь & ndash; std :: move и std :: forward - очевидные исключения. Если бы вы возвращали ссылку на rvalue, вы бы взломали код вроде:
for (char c : my_string + other_string) {
//...
}
Этот цикл ведет себя (согласно 6.5.4 / 1 в N3092), как если бы код был:
auto&& range = my_string + other_string;
Это, в свою очередь, приводит к зависанию. Время жизни временного объекта не увеличивается, потому что ваш оператор + не возвращает prvalue . Возврат объектов по значению прекрасно. Это создаст временные объекты, но эти объекты являются значениями, поэтому мы можем украсть их ресурсы, чтобы сделать его очень эффективным.
Во-вторых, ваш код также не должен компилироваться по той же причине, по которой он не будет компилироваться:
int&& foo(int&& x) { return x; }
Внутри тела функции x является lvalue, и вы не можете инициализировать «возвращаемое значение» (в данном случае ссылку rvalue) с помощью выражения lvalue. Итак, вам нужно явное приведение.
В-третьих, вам не хватает const & + const & overload. Если оба ваших аргумента являются lvalues, компилятор не найдет используемый оператор + в вашем случае.
Если вы не хотите так много перегрузок, вы также можете написать:
S operator+(S value, S const& x)
{
value += x;
return value;
}
Я специально не писал return value+=x;
, потому что этот оператор, вероятно, возвращает ссылку на lvalue, которая привела бы к созданию копии возвращаемого значения. С двумя написанными мной строками возвращаемое значение будет построено из значения .
S x = a + b + c + d;
По крайней мере, этот случай очень эффективен, поскольку нет необходимости в копировании, даже если компилятор не может удалить копии & ndash; благодаря классу строк с поддержкой перемещения. На самом деле, с таким классом, как std :: string, вы можете использовать функцию-член быстрой замены и сделать ее эффективной и в C ++ 03, при условии, что у вас есть достаточно умный компилятор (например, GCC):
S operator+(S value, S const& x) // pass-by-value to exploit copy elisions
{
S result;
result.swap(value);
result += x;
return result; // NRVO applicable
}
См. Статью Дэвида Абрахама Хотите скорость? Передать по значению . Но эти простые операторы не будут такими эффективными, учитывая:
S x = a + (b + (c + d));
Здесь левая часть оператора всегда lvalue. Так как оператор + занимает левую часть по значению, это приводит ко многим копиям Четыре перегрузки сверху прекрасно справляются и с этим примером.
Прошло много времени с тех пор, как я прочитал старую напыщенную речь Линуса. Если он жаловался на ненужные копии в отношении std :: string, эта жалоба больше не действительна в C ++ 0x, но раньше она была недействительной. Вы можете эффективно объединить много строк в C ++ 03:
S result = a;
result += b;
result += c;
result += d;
Но в C ++ 0x вы также можете использовать операторы + и std :: move. Это тоже будет очень эффективно.
Я действительно посмотрел исходный код Git и его управление строками (strbuf.h). Это выглядит хорошо продуманным. За исключением функции отсоединения / присоединения, вы получаете то же самое с std :: string с включенным перемещением, с очевидным преимуществом в том, что ресурс автоматически управляется самим классом по сравнению с пользователем, которому нужно помнить, чтобы вызывать нужные функции в правильные времена (strbuf_init, strbuf_release).