У меня есть вопрос о «хороших методах проектирования» в C ++. Я пишу числовую библиотеку на C ++ 11 и использую много метапрограммирования и основанную на шаблонах технику. Но у меня есть очень простой вопрос:
Рассмотрим функцию, которая может иметь два очень близких поведения за исключением опции, которая может быть активирована с помощью логического флага. Я рассматриваю только флаг, который может быть установлен / снят разработчиком, а не флаг, который может быть установлен / снят во время выполнения. Есть 3 варианта дизайна:
1) Напишите две функции с явной опцией в их имени:
myFunctionFlag1(...);
myFunctionFlag2(...);
2) Используйте параметр шаблона:
template<bool Flag> myFunction(...);
3) Используйте переменный параметр:
myFunction(..., const bool flag);
С точки зрения надлежащей практики проектирования, какое решение является приемлемым / неприемлемым? Если есть лучшее решение, какое это и почему? Если есть худшее решение, какое и почему?
РЕДАКТИРОВАТЬ: для рассматриваемых функций время выполнения может рассматриваться как незначительное, так что это не самая критическая точка.
РЕДАКТИРОВАТЬ 2: Я знаю, что все три работают. Но так как у моей библиотеки будут пользователи, она должна иметь надежный / хороший дизайн.
Является ли вариант 2 распространенным (потому что он мне кажется хорошим компромиссом)?
Ни один из вышеперечисленных. Булевы флаги ужасны для читабельности кода. Если управляемая флагом функциональность достаточно мала, поэтому имеет смысл использовать одну функцию, тогда используйте одну функцию, но не используйте bool
как тип флага. Вместо этого используйте перечисление с перечислителями, которые имеют полезные имена:
enum class MyFunctionMode { EnableFoo, DisableFoo };
void myFunction(..., MyFunctionMode mode);
Этот шаблон позволяет легко понять на месте вызова, какие опции предоставляются функции. Несколько вариантов могут быть объединены с использованием флагов.
Я рекомендую использовать вариант 1.
Комментарии:
Мои 2 цента.
Ваше первое решение дублирует код, и это то, что никто не хочет делать.
В третьем случае это приемлемо, но оператор if будет оцениваться во время выполнения и подразумевает потерю времени.
Шаблон один кажется подходящим.
Оператор if будет другим if (true)
или же if (false)
во время компиляции, и компилятор оптимизирует его, как если бы они были оператором if.
Вы можете сделать все три, что вы знаете.
void fun_that_does_stuff();
void fun_that_does_something_else();
template < bool F >
void fun_that_does_stuff_or_something();
template < >
void fun_that_does_stuff_or_something<true>() { fun_that_does_stuff(); }
... blah blah.
Дублирование нетривиального кода — худший вариант. Если две функции делают немного больше, чем просто отправляют интересную функцию, это может быть правильным вариантом. Конечно, две функции могут отправляться в шаблонную версию, но они могут дать лучшее представление о том, в чем разница true
или же false
может сделать.
Другая проблема заключается в том, как на самом деле используются функции: если они сами вызываются из функций, имеющих одинаковые конфигурации или конфигурации, из которых может быть получен выбор флагов, использование разных имен становится довольно раздражающим или даже не обслуживаемым: в конечном итоге вы получите необходимость вызывать функции условно:
(flag? &myFunctionFlag1: &myFunctionFlag2)(...);
Выбор между двумя другими версиями сводится к вопросу: имеет ли значение проверка? Если это так, шаблон является единственным выбором. Если этого не произойдет, вы можете выбрать любую версию, и я бы поспорил, что анонимный опрос проголосует за версию во время выполнения: глаза большинства моих коллег поглядывают, когда я показываю им тривиальный шаблон.