2010-09-25 4 views
4

Примечание: как отмечено sellibitze. Я не обновляюсь на ссылках rvalues, поэтому методы, которые я предлагаю, содержат ошибки, читайте его anwser, чтобы понять, что именно.Устранение временных ситуаций при перегрузке оператора

Вчера я читал один из Linus' rant, и есть (где-то) напыщенная речь о перегрузке оператора.

жалоба, кажется, что если у вас есть объект типа S затем:

S a = b + c + d + e; 

может включать в себя множество временных.

В C++ 03, у нас есть копия Пропуска, чтобы предотвратить это:

S a = ((b + c) + d) + e; 

Я хотел бы надеяться, что последний ... + e оптимизирован, но мне интересно, сколько создаются временные конструкции с определенным пользователем: operator+.

Кто-то из этой темы предложил использовать шаблоны выражений для решения проблемы.

Теперь эта тема восходит к 2007 году, но в наши дни, когда мы думаем об устранении временных ситуаций, мы думаем, что Move.

Так что я думал о множестве операторов перегрузки, о которых мы должны написать, чтобы не исключать временные, но чтобы ограничить стоимость их строительства (кражи ресурсов).

S&& operator+(S&& lhs, S const& rhs) { return lhs += rhs; } 
S&& operator+(S const& lhs, S&& rhs) { return rhs += lhs; } // * 
S&& operator+(S&& lhs, S&& rhs) { return lhs += rhs; } 

Достаточно ли этого набора операторов? Разве это обобщаемо (на ваш взгляд)?

*: эта реализация допускает коммутативность, она не работает для печально известного string.

+0

Не задано в вопросе: если этого набора достаточно, это означает, что у нас есть 3 новых перегрузки для каждого оператора ... И кроме использования Boost.Operator я не вижу возможности автоматически их генерировать. Из верхней части моей головы это означает 8 'operator +' для 'std :: string' из-за' std :: string' и 'char const *' mix ... –

+0

Ух, это ужасно. И они называют это «решением».Pfft –

+0

@ litb: Я согласен с вашим чувством ... особенно когда я думаю о '-',' * ','/'и всех других подобных операторах. У числовых классов будет гораздо больше оснований наследовать от 'boost :: addable' и co, я думаю. –

ответ

4

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

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 ​​, как правило, очень опасная вещь   –   std :: move and std :: forward - очевидные исключения. Если вы должны были вернуть ссылку RValue вы разобьете код, как:

for (char c : my_string + other_string) { 
    //... 
} 

Эта петля ведет себя (в соответствии с 6.5.4/1 в N3092), а если код:

auto&& range = my_string + other_string; 

Это в свою очередь, приводит к обманутой ссылке. Срок службы временного объекта не продлевается, потому что ваш оператор + не возвращает prvalue. Возвращение объектов по значению отлично. Это создаст временные объекты, но эти объекты имеют значения rvalues, поэтому мы можем украсть их ресурсы, чтобы сделать их очень эффективными.

Во-вторых, ваш код не должен также составить по той же причине, что это не будет компилироваться:

int&& foo(int&& x) { return x; } 

Внутри тела функции х является Левое и вы не можете инициализировать «возвращаемое значение» (в данном случае ссылку rvalue) с выражением lvalue. Итак, вам нужен явный приведение.

В-третьих, вам не хватает константы & + const & перегрузка. Если оба аргумента равны lvalues, компилятор не найдет в вашем случае пригодного для использования оператора +.

Если вы не хотите так много перегрузок, можно был бы написать:

S operator+(S value, S const& x) 
{ 
    value += x; 
    return value; 
} 

Я намеренно не писал return value+=x;, потому что этот оператор, вероятно, возвращает Lvalue ссылку, которая привела бы скопировать строительство возвращаемое значение. С двумя строками, которые я написал, возвращаемое значение будет двигаться, построенное из значения .

S x = a + b + c + d; 

По крайней мере, этот случай является очень эффективным, потому что нет лишнего копирования участвует даже если компилятор не может игнорировать копии   –   благодаря ходу с поддержкой строки класса. На самом деле, с классом, как станд :: строки вы можете использовать свою функцию-член быстрой подкачки и сделать его эффективным в 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 
} 

См Давид Статья Авраама Want Speed? Pass by Value. Но эти простые операторы не будут столь эффективными Дано:

S x = a + (b + (c + d)); 

Здесь левая рука оператора всегда именующий. Так как оператор + берет свою левую сторону по значению, это приводит ко многим копиям. Четыре перегрузки сверху тоже отлично справляются с этим примером.

Прошло некоторое время с тех пор, как я прочитал старый рассказ Линуса. Если он жаловался на ненужные копии в отношении std :: string, эта жалоба более недействительна в C++ 0x, но раньше она вряд ли была действительной. Вы можете эффективны конкатенация много строк в C++ 03:

S result = a; 
result += b; 
result += c; 
result += d; 

Но в C++ 0x можно также использовать оператор + и зЬй :: двигаться. Это тоже будет очень эффективно.

Я действительно посмотрел исходный код Git и его управление строкой (strbuf.h). Это выглядит хорошо продумано. За исключением функции отсоединения/присоединения, вы получаете то же самое с поддержкой std :: string с поддержкой перехода с очевидным преимуществом, что ресурс, который он автоматически управляет самим классом, в отличие от пользователя, которому необходимо запомнить правильные функции на правильные времена (strbuf_init, strbuf_release).

+0

Спасибо, что поймал ошибку (возвращающ '&&'). Я не имею возможности играть с rvalue-ссылками, но я не обновляюсь там. Это тем не менее означает 4 перегрузки, чтобы справиться с этим ... мой ... –

Смежные вопросы