我目前正在开展一个项目,我遇到了以下问题。
我有一个C ++方法,我希望以两种不同的方式工作:
void MyFunction()
{
foo();
bar();
foobar();
}
void MyFunctionWithABonus()
{
foo();
bar();
doBonusStuff();
foobar();
}
我不想复制我的代码,因为实际功能要长得多。 问题是,在调用MyFunction而不是MyFunctionWithABonus时,我不能在任何情况下向程序添加执行时间。这就是为什么我不能只有一个布尔参数,我用C ++比较检查。
我的想法是使用C ++模板来虚拟复制我的代码,但我想不出一种方法,我没有额外的执行时间,我不必复制代码。
我不是模板专家所以我可能会遗漏一些东西。
你们有没有想法?或者这在C ++ 11中是不可能的?
答案 0 :(得分:129)
这样的事情会做得很好:
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>(); }
你可以找到关于该技术的一些很好的信息there。这是一篇“旧”论文,但这项技术本身就完全正确。
如果您可以访问一个不错的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();
}
答案 1 :(得分:55)
使用模板和lambda,您可以:
prefix
或者您可以创建suffix
和void prefix()
{
foo();
bar();
}
void suffix()
{
foobar();
}
void MyFunction()
{
prefix();
suffix();
}
void MyFunctionWithABonus()
{
prefix();
doBonusStuff();
suffix();
}
功能。
quotechar
答案 2 :(得分:27)
鉴于OP在调试方面做出的一些评论,这里有一个版本调用doBonusStuff()
用于调试版本,而不是版本构建版本(用于定义NDEBUG
):
#if defined(NDEBUG)
#define DEBUG(x)
#else
#define DEBUG(x) x
#endif
void MyFunctionWithABonus()
{
foo();
bar();
DEBUG(doBonusStuff());
foobar();
}
如果您希望检查条件,也可以使用assert
macro,如果条件为假,则可以使用{(3);但仅适用于调试版本;发布版本不会执行检查。
如果doBonusStuff()
有副作用,请小心,因为这些副作用在发布版本中不会出现,并且可能会使代码中的假设无效。
答案 3 :(得分:18)
以下是使用可变参数模板的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);
答案 4 :(得分:11)
另一个版本,只使用模板而没有重定向功能,因为你说你不想要任何运行时开销。正如我所关注的那样,这只会增加编译时间:
#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
参数作为模板参数。
答案 5 :(得分:8)
您可以使用标记调度和简单函数重载:
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
条款 - 通过添加更多标签),当然也不会留下运行时间。
调用语法非常友好,但当然可以包含在vanilla调用中:
void MyFunctionAlone() { MyFunction(Tag_DisableBonus{}); }
void MyFunctionBonus() { MyFunction(Tag_EnableBonus{}); }
标签调度是一种广泛使用的通用编程技术,here是一篇关于基础知识的好文章。