Дублирование кода с помощью c++11


в настоящее время я работаю над проектом, и у меня следующий вопрос.

У меня есть метод C++, который я хочу работать двумя разными способами:

void MyFunction()
{
  foo();
  bar();
  foobar();
}

void MyFunctionWithABonus()
{
  foo();
  bar();
  doBonusStuff();
  foobar();
}

и я хотел бы, чтобы не дублировать мой код, потому что реально гораздо больше. Проблема в том, что я ни при каких обстоятельствах не должен добавлять время выполнения в программу, когда MyFunction вызывается вместо MyFunctionWithABonus. Вот почему я не могу просто иметь логический параметр, который я проверяю с помощью C++ сравнение.

моя идея состояла бы в том, чтобы использовать шаблоны C++ для виртуального дублирования моего кода, но я не могу придумать способ, в котором у меня нет дополнительного времени выполнения, и мне не нужно дублировать код.

Я не эксперт с шаблонами, поэтому я могу что-то упустить.

у кого-нибудь есть идея? Или это просто невозможно в C++11?

6 80

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{}); }

отправка тегов-это широко используемый общий метод программирования, здесь хороший пост об основах.