使用 c + + 11复制代码

我目前正在做一个项目,我有以下问题。

我有一个 C + + 方法,我想以两种不同的方式工作:

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


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

我不想重复我的代码,因为实际的函数要长得多。 问题是,在任何情况下,当调用 MyFunction 而不是 MyFunctionWithABonus 时,我都不能给程序增加执行时间。这就是为什么我不能只用 C + + 比较来检查布尔参数的原因。

我的想法是使用 C + + 模板来实际复制我的代码,但是我想不出一种不需要额外的执行时间也不需要复制代码的方法。

我不是模板方面的专家,所以我可能遗漏了一些东西。

你们有人有什么想法吗? 或者这在 C + + 11中是不可能的?

8235 次浏览

使用 template 和 lambda,您可以:

template <typename F>
void common(F f)
{
foo();
bar();
f();
foobar();
}


void MyFunction()
{
common([](){});
}


void MyFunctionWithABonus()
{
common(&doBonusStuff);
}

或者你可以只创建 prefixsuffix函数。

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编译器,你甚至可以进一步推动这项技术,通过使用 如果,像这样:

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

另一个版本,只使用模板,不使用重定向函数,因为您说过不需要任何运行时开销。就我而言,这只会增加编译时间:

#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()使用 bool参数作为模板参数。

下面是 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);

考虑到 OP 对调试的一些评论,这里有一个版本调用 doBonusStuff()进行调试构建,但不调用发行版构建(定义 NDEBUG) :

#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif


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

如果希望检查某个条件,也可以使用 assert,如果条件为 false 则失败(但仅限于调试版本; 发布版本将不执行检查)。

如果 doBonusStuff()有副作用,要小心,因为这些副作用不会出现在发布版本中,并且可能使代码中的假设失效。

你可以使用 标记分派标记分派和简单的函数过载:

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

标签分配是一种被广泛使用的泛型技术,给你是一篇很好的基础文章。