Можно ли в C++ перезагрузить оператор уже существующего класса?
Например:
///////////////////
//////////////////
std::vector<int> a = {9, 8, 7};
std::vector<int> b = {1, 3, 4};
std::vector<int> c = a + b;
for (int i = 0; i < c.size(); i = i + 1) {
std::cout << c[i] << " ";
}
//10 11 11//
Копирования вектора лишний раз не будет, будет перемещение. Так что произойдет три new и три delete (см дизасм).
Что касается того, что быстрее, сначала скопировать, потом суммировать, или просто суммировать, здесь тоже есть нюанс - если данные сначала скопировать, их локальность будет выше при последующем суммировании.
(По крайней мере, мой вариант порождает более короткий выхлоп в машинном коде)
Более того, использование push_back создает для компилятора две интересные беды (см. бенч)
1) На каждый push_back он будет вынужден делать проверку, а не кончилось ли место в векторе.
2) Будет труднее применить оптимизацию при помощи векторных инструкций процессора.
Желтый столбик - это вариант с push_back и он очень сильно болен
Синий столбик - это ваш исправленный вариант, в котором убран push_back
Розовый - мой вариант, как в ответе.
Wataru, после починки push_back вариант без result все равно оказывается чуточку быстрее, несмотря на, казалось бы, лишнее копирование - за счет лучшей локальности данных, как я понимаю.
Впрочем, это уже сильно зависит от того, что хранится в векторе.
Армянское Радио, Не, ну без push_back там инициализация нулями, которая не быстрее копирования будет.
Правда почему оно на 10% медленнее вашего варианта - я пока не понимаю.
Армянское Радио, Как же не делает никакой инициализации? Он же конструктор по умолчанию вызывает. Ах, да, вот почему оно, похоже, медленее. Если оптимизация не догадывается сделать memset с 0, то это точно будет медленее memcpy который происходит в вашем варианте.
Там действительно есть вызов memset, но это какая-то гадость - если делать ресайз, будет делаться это зануление бестолковое, а если не делать ресайз, будет трудно воспользоваться векторизацией.
Армянское Радио, я тоже залезу со своим советом. :) Общая форма внешней перегрузки оператора сложения выглядит как T operator+(const T &a, const T2 &b);.
Принимает две константные ссылки и возвращает значение. Нарушать эту форму трансляторы позволяют, но читатели такого кода в восторге не будут, особенно от содержимого оператора.
Семантическая прозрачность кода - это то, чему надо учиться с самого начала и учить с самого начала других. В этом плане я бы рекомендовал прививать студенту навык писать код исключительно в рамках стандарта.
std::vector<int> operator + ( const std::vector<int>& left, const std::vector<int>& right )
{
if( left.size() != right.size() )
{
throw std::range_error("sizes mismatch!");
}
std::vector<int> result{ left };
for( size_t index = 0; index < right.size(); ++index )
{
result[ index ] += right[ index ];
}
return result;
}
Суть не поменялась, скорость тоже. А понятность - в разы выросла. Вопросов меньше, а удобства чтения - больше.
Евгений Шатунов, остается только пожелать разработчикам стандарта и компиляторов дальнейших творческих успехов.
P.S. И выдать медаль в виде чугунного люка изоборетателю std::numeric_limits::min() - за самую неинтуитивную и веселую подставу от C++ в моей личной истории:
For floating-point types with denormalization, min returns the minimum positive normalized value. Note that this behavior may be unexpected, especially when compared to the behavior of min for integral types. To find the value that has no values less than it, use numeric_limits::lowest.
Армянское Радио, на деле, в своем коде я стараюсь придерживаться именно стандарта. GCC много раз был уличен в вольности трактовок правил стандарта. Поэтому я исключил его из поддержки своими продуктами еще в 2016 году. NDK у Android пошли тем же путем. Потом GCC уже исправился и убрал свои вольности, но "осадочек", как говорится, остался.
Я это не к тому что GCC плохо и его надо сжечь. Я к тому, что лучше свой код выражать всегда в терминах стандарта просто потому что с каждым годом трансляторы все больше и больше следуют именно ему, отказываясь от своих вольных трактовок. Даже cl от майков идет этой дорогой.
Статься может так, что завтра твой вариант инлайниться перестанет и на GCC тоже, а мой - начнет.
По поводу std::numeric_limits::min() можно сказать только то, что FLT_MIN даже в C означает ровно то же самое.
Армянское Радио,
you didn't read right
if( left.size() != right.size() )
{
throw std::range_error("sizes mismatch!");
}
will remain, but its intent, really, is to be performed before "call", not after.
These checks should be part of a contract, not execution steps inside function body.
Point is, innovations running ahead of "standards" are needed. Whoever gets those right drives standard forward.