У меня есть класс Writer
с такой функцией writeVector
:
void Drawer::writeVector(vector<T> vec, bool index=true)
{
for (unsigned int i = 0; i < vec.size(); i++) {
if (index) {
cout << i << "\t";
}
cout << vec[i] << "\n";
}
}
Я стараюсь не дублировать код, но при этом беспокоюсь о производительности. В этой функции я if (index)
проверяю каждый раунд своего for
цикла, хотя результат всегда один и тот же. Это против того, чтобы «беспокоиться о производительности».
Я мог бы легко избежать этого, разместив проверку за пределами моего for
цикла. Однако я получу кучу повторяющегося кода:
void Drawer::writeVector(...)
{
if (index) {
for (...) {
cout << i << "\t" << vec[i] << "\n";
}
}
else {
for (...) {
cout << vec[i] << "\n";
}
}
}
Так что для меня это оба «плохих» решения. Я думал о двух частных функциях, одна из которых выходит за пределы индекса, а затем вызывает другую. Другой только превосходит ценность. Однако я не могу понять, как использовать его с моей программой, мне все равно нужна if
проверка, чтобы узнать, какую из них вызывать ...
Согласно проблеме, полиморфизм кажется правильным решением. Но я не понимаю, как мне его здесь использовать. Каким будет предпочтительный способ решения такой проблемы?
Это не настоящая программа, мне просто интересно узнать, как следует решать такого рода проблемы.
источник
Ответы:
Передайте тело цикла как функтор. Он встроен во время компиляции, без потери производительности.
Идея передачи того, что варьируется, повсеместно присутствует в стандартной библиотеке C ++. Это называется паттерном стратегии.
Если вам разрешено использовать C ++ 11, вы можете сделать что-то вроде этого:
Этот код не идеален, но идею вы поняли.
В старом C ++ 98 это выглядело так:
Опять же, код далек от совершенства, но он дает вам идею.
источник
for(int i=0;i<100;i++){cout<<"Thank you!"<<endl;}
: D Это то решение, которое я искал, оно работает как шарм :) Вы могли бы улучшить его с помощью нескольких комментариев (сначала были проблемы с его пониманием), но у меня это получилось, поэтому нет проблем :)cout << e << "\n";
), все равно было бы некоторое дублирование кода.Если это действительно так, у предсказателя ветвления не будет проблем с предсказанием (постоянного) результата. Таким образом, это приведет лишь к незначительным накладным расходам из-за неверных прогнозов на первых нескольких итерациях. Не о чем беспокоиться с точки зрения производительности
В этом случае я рекомендую держать тест внутри цикла для ясности.
источник
std::cout
функция)Чтобы расширить ответ Али, который совершенно правильный, но все же дублирует некоторый код (часть тела цикла, этого, к сожалению, вряд ли можно избежать при использовании шаблона стратегии) ...
Допустим, в этом конкретном случае дублирование кода невелико, но есть способ еще больше уменьшить его, что пригодится, если тело функции больше, чем несколько инструкций .
Ключ в том, чтобы использовать способность компилятора выполнять постоянное сворачивание / устранение мертвого кода . Мы можем сделать это, вручную сопоставив значение времени выполнения со значением времени
index
компиляции (это легко сделать, когда есть только ограниченное количество случаев - в данном случае два) и использовать аргумент шаблона без типа, который известен при компиляции -время:Таким образом, мы получаем скомпилированный код, который эквивалентен вашему второму примеру кода (внешний
if
/ внутреннийfor
), но без дублирования кода. Теперь мы можем сделать версию шаблонаwriteVector
настолько сложной, насколько захотим, всегда будет один фрагмент кода, который нужно поддерживать.Обратите внимание на то, как версия шаблона (которая принимает константу времени компиляции в форме аргумента шаблона, не являющегося типом) и версия без шаблона (которая принимает переменную времени выполнения в качестве аргумента функции) перегружены. Это позволяет вам выбрать наиболее подходящую версию в зависимости от ваших потребностей, имея довольно похожий, легко запоминающийся синтаксис в обоих случаях:
источник
doWriteVector
напрямую, но я согласен, что имя было неудачным. Я просто изменил его, чтобы иметь две перегруженныеwriteVector
функции (одну шаблонную, другую - обычную), чтобы результат был более однородным. Спасибо за предложение. ;)В большинстве случаев ваш код уже хорош по производительности и удобочитаемости. Хороший компилятор способен обнаруживать инварианты цикла и выполнять соответствующие оптимизации. Рассмотрим следующий пример, очень близкий к вашему коду:
Я использую следующую командную строку для его компиляции:
Затем дамп сборки:
Результат сборки
write_vector
:Мы видим, что в начале функции мы проверяем значение и переходим к одному из двух возможных циклов:
Конечно, это работает только в том случае, если компилятор способен определить, что условие фактически инвариантно. Обычно он отлично работает с флагами и простыми встроенными функциями. Но если состояние «сложное», рассмотрите возможность использования подходов из других ответов.
источник