Является ли перегрузка оператора сложения ссылкой на rvalue в качестве его левого операнда хорошей практикой?

Предполагая, что str является классом для хранения строковых значений, он перегрузит оператор сложения, чтобы поддерживать конкатенацию строк. Нравится:

str operator+(const str &a,const str &b);

Но проблема в том, что если у нас есть что-то вроде этого:

str s=str("Hel") + str("lo ") + str("Wor") + str("ld!");

Затем он создаст 3 временных объекта (поскольку мы получаем новый объект при каждом добавлении), которые на самом деле не нужны в этом контексте. Простым решением этой проблемы может быть перегрузка нового оператора сложения, который принимает ссылку на rvalue в качестве левого операнда и возвращает этот операнд также как ссылку на rvalue после объединения его с правым операндом. Что-то вроде этого:

str &&operator+(str &&a,const str &b){
   a+=b;
   return std::move(a);
}

Перегрузив этот оператор, упомянутый оператор просто создаст один временный объект, а следующие добавления будут просто объединены с этим временным объектом.

Мой вопрос в том, является ли этот метод правильным решением этой проблемы?


person Keyvan Kambakhsh    schedule 31.08.2015    source источник
comment
Если вы хотите избежать временных, избегайте создания «str» в первую очередь - используйте операторы «str» + «const char*»,...   -  person    schedule 31.08.2015
comment
Это действительно то, для чего были разработаны операции перемещения. Прямо на месте.   -  person SergeyA    schedule 31.08.2015
comment
@DieterLücking, ты не прав. str(s) + a + b + c + d приведет к созданию большого количества временных файлов.   -  person SergeyA    schedule 31.08.2015
comment
@SergeyA Даже без C++11 должен быть только один (после =)   -  person    schedule 31.08.2015
comment
@DieterLücking, конечно, нет. Каждый + будет производить временный.   -  person SergeyA    schedule 31.08.2015
comment
Простейшим способом объединения нескольких строк было бы использование какого-либо построителя строк (например, std::ostringstream). В противном случае каноническим решением будет перегрузить operator+= и написать operator+, взяв левое значение по значению относительно него.   -  person Alexandre C.    schedule 31.08.2015


Ответы (1)


Струны обычно можно эффективно перемещать.

Таким образом, ваш operator+ должен возвращать str, а не str&&. Это означает, что

str const& bob = str1 + str2;

не ломается ужасно или более правдоподобно:

for(char c : str1 + str2)

ужасно не ломается.

Во-вторых, просто возьмите str по значению. Если это значение r, оно будет перемещено (дешево). Если это lvalue, оно будет скопировано (затем расширено, а затем возвращено). Тоже дешево.

str operator+( str lhs, str const& rhs )

наконец, вы можете сделать rhs типом шаблона чего-либо, конвертируемого в str (или действительного для добавления в str), чтобы удалить, возможно, бесполезные преобразования.

Особенно

str a = str("Hello") + " world";

должен построить "Hello", затем добавить " world" без создания другого объекта str, а затем переместить этот результат в a.

Вы могли бы написать +, который симметрично обрабатывает rvalue как слева, так и справа, но это работа, и по иронии судьбы цепочка + помещает rvalue слева из-за того, как цепочка + связывает свои аргументы.

Наконец, вы можете пройти весь путь до шаблонов выражений и (почти) ничего не делать, пока результат не будет назначен. Это опасно и не стоит того, чтобы делать что-то такое простое, как класс игрушечных струн. Если вы пишете серьезный класс цепочки символов, это может быть полезно после того, как вы сделали кучу других улучшений.

person Yakk - Adam Nevraumont    schedule 31.08.2015
comment
Хотя это тормозит РВО. В случае дешевого переносного шрифта это, вероятно, не имеет значения, но об этом стоит помнить. Кроме того, зачем оптимизировать rvalue только для LHS, а не для RHS? - person juanchopanza; 31.08.2015
comment
@juan lazy (больше работы), плюс + связывает такое, что цепочка rvalues ​​слева (по иронии судьбы). - person Yakk - Adam Nevraumont; 31.08.2015
comment
Спасибо за ваш ответ, но моя проблема в том, что если я даже использую что-то вроде этого: str (hello) + wo + r + l + d, тогда для каждого «+» в выражении будет временный объект! Я хочу избежать этого. - person Keyvan Kambakhsh; 01.09.2015
comment
@Saturn будут временные объекты, но их внутреннее состояние будет вырвано и регулярно использовано повторно, если вы сделаете их эффективными для перемещения. Создается str("hello"), затем он перемещается, затем добавляется +" wo", затем он перемещается, затем добавляется "r", затем он перемещается и т. д. Каждое перемещение выполняется в новый временный объект, но при этом создается строковый тип via-move ( должно быть) грязно дешево. Предполагая, что ваше хранилище представляет собой std::vector или что-то разумное, эти перемещения представляют собой несколько копий указателя и нулей; выделения не происходит. И приличный компилятор может даже отказаться (не обязательно). - person Yakk - Adam Nevraumont; 01.09.2015