Дублирование кода с помощью c++11
в настоящее время я работаю над проектом, и у меня следующий вопрос.
У меня есть метод C++, который я хочу работать двумя разными способами:
void MyFunction()
{
foo();
bar();
foobar();
}
void MyFunctionWithABonus()
{
foo();
bar();
doBonusStuff();
foobar();
}
и я хотел бы, чтобы не дублировать мой код, потому что реально гораздо больше. Проблема в том, что я ни при каких обстоятельствах не должен добавлять время выполнения в программу, когда MyFunction вызывается вместо MyFunctionWithABonus. Вот почему я не могу просто иметь логический параметр, который я проверяю с помощью C++ сравнение.
моя идея состояла бы в том, чтобы использовать шаблоны C++ для виртуального дублирования моего кода, но я не могу придумать способ, в котором у меня нет дополнительного времени выполнения, и мне не нужно дублировать код.
Я не эксперт с шаблонами, поэтому я могу что-то упустить.
у кого-нибудь есть идея? Или это просто невозможно в C++11?
6 ответов:
с шаблоном и лямбда, вы можете сделать:
template <typename F> void common(F f) { foo(); bar(); f(); foobar(); } void MyFunction() { common([](){}); } void MyFunctionWithABonus() { common(&doBonusStuff); }
или же вы можете просто создать
prefix
и .void prefix() { foo(); bar(); } void suffix() { foobar(); } void MyFunction() { prefix(); suffix(); } void MyFunctionWithABonus() { prefix(); doBonusStuff(); suffix(); }
что-то вроде этого будет хорошо:
template<bool bonus = false> void MyFunction() { foo(); bar(); if (bonus) { doBonusStuff(); } foobar(); }
вызова через:
MyFunction<true>(); MyFunction<false>(); MyFunction(); // Call myFunction with the false template by default
" уродливый " шаблон можно все избежать, добавив некоторые хорошие обертки к функциям:
void MyFunctionAlone() { MyFunction<false>(); } void MyFunctionBonus() { MyFunction<true>(); }
вы можете найти некоторые хорошие сведения об этой технике здесь. Это "старая" бумага, но сама по себе техника остается совершенно правильной.
если у вас есть доступ к хорошему компилятору C++17, вы можете даже продвинуть эту технику, используя constexpr if, вот так:
template <int bonus> auto MyFunction() { foo(); bar(); if constexpr (bonus == 0) { doBonusStuff1(); } else if constexpr (bonus == 1) { doBonusStuff2(); } else if constexpr (bonus == 2) { doBonusStuff3(); } else if constexpr (bonus == 3) { doBonusStuff4(); } // Guarantee that this function will not compile // if a bonus different than 0,1,2,3 is passer else { static_assert(false);}, foorbar(); }
учитывая некоторые комментарии, сделанные OP относительно отладки, вот версия, которая вызывает
doBonusStuff()
для отладочных сборок, но не выпускает сборки (которые определяютNDEBUG
):#if defined(NDEBUG) #define DEBUG(x) #else #define DEBUG(x) x #endif void MyFunctionWithABonus() { foo(); bar(); DEBUG(doBonusStuff()); foobar(); }
вы также можете использовать
assert
макрос если вы хотите проверить условие и, если оно ложно (но только для отладочных сборок; выпусков не выполнить проверку).будьте осторожны, если
doBonusStuff()
имеет побочных эффектов, так как эти побочные эффекты не будут присутствовать в релизе создает и может аннулировать допущения, сделанные в коде.
вот небольшое изменение ответа Jarod42 с использованием вариативных шаблонов, поэтому вызывающий может предоставить ноль или одну бонусную функцию:
void callBonus() {} template<typename F> void callBonus(F&& f) { f(); } template <typename ...F> void MyFunction(F&&... f) { foo(); bar(); callBonus(std::forward<F>(f)...); foobar(); }
телефонный код:
MyFunction(); MyFunction(&doBonusStuff);
другая версия, используя только шаблоны и никаких функций перенаправления, так как вы сказали, что не хотите никаких накладных расходов во время выполнения. Что касается fas, то это только увеличивает время компиляции:
#include <iostream> using namespace std; void foo() { cout << "foo\n"; }; void bar() { cout << "bar\n"; }; void bak() { cout << "bak\n"; }; template <bool = false> void bonus() {}; template <> void bonus<true>() { cout << "Doing bonus\n"; }; template <bool withBonus = false> void MyFunc() { foo(); bar(); bonus<withBonus>(); bak(); } int main(int argc, const char* argv[]) { MyFunc(); cout << "\n"; MyFunc<true>(); } output: foo bar bak foo bar Doing bonus bak
теперь есть только одна версия
MyFunc()
С
можно использовать тег диспетчеризации и простая перегрузка функции:
struct Tag_EnableBonus {}; struct Tag_DisableBonus {}; void doBonusStuff(Tag_DisableBonus) {} void doBonusStuff(Tag_EnableBonus) { //Do bonus stuff here } template<class Tag> MyFunction(Tag bonus_tag) { foo(); bar(); doBonusStuff(bonus_tag); foobar(); }
Это легко читать / понимать, может быть расширен без пота (и не шаблонный
if
предложения-путем добавления дополнительных тегов), и, конечно же, не оставит след во время выполнения.синтаксис вызова он довольно дружелюбен, но, конечно, может быть обернут в ванильные вызовы:
void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); } void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); }
отправка тегов-это широко используемый общий метод программирования, здесь хороший пост об основах.