2017-04-25 68 views
80

我目前正在一个项目上,我有以下问题。使用c + +重复的代码11

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

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

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

而且我想不要重复我的代码,因为实际的功能是更长的时间。 问题是我不能在任何情况下调用MyFunction而不是MyFunctionWithABonus来为程序添加执行时间。这就是为什么我不能只用一个C++比较来检查布尔参数。

我的想法本来是使用C++模板来虚拟复制我的代码,但我想不到一种做法,我没有额外的执行时间,我不必复制代码。

我不是模板专家,所以我可能会错过一些东西。

你们有没有想法?或者,这在C++ 11中是不可能的?

+64

我可以问*为什么*你不能简单地添加一个布尔检查?如果那里有很多代码,那么简单布尔检查的开销就会忽略不计。 – Joris

+0

这个函数会在性能非常重要的上下文中多次调用。基本上,doBonusStuff()方法必须在调试环境中调用,目标不是在非调试环境中添加任何运行时。 – plougue

+39

@plougue现在分支预测非常好,直到布尔检查通常需要执行0个处理器周期。 – Dan

回答

55

随着模板和λ,你可以这样做:

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

我实际上喜欢这两个解决方案通过布尔参数(模板或其他),无论任何执行时间的优势。我不喜欢布尔参数。 –

+2

根据我的理解,第二种解决方案由于附加的函数调用而具有额外的运行时间。这是第一个例子吗?我不确定lambda在这种情况下的工作方式 – plougue

+10

如果定义可见,编译器可能会内联代码并生成与原始代码生成的代码相同的代码。 – Jarod42

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

You ca ñ找到一些很好的信息在该技术there。这是一个“老”的论文,但这项技术本身保持完全正确。

只要你有机会获得一个不错的C++编译器17,你甚至可以进一步推技术,通过使用constexpr如果,这样的:

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

如果使用constexpr(bonus){doBonusStuff();那么编译器 – Jonas

+21

和[在C++ 17](https://wandbox.org/permlink/0NW2N0nvuZNKWOeG) }'。 –

+5

@ChrisDrew不确定constexpr是否会在这里添加任何东西。会吗? – Gibet

11

另一个版本,只使用模板和无重定向功能,因为你说你不想要任何运行时开销。由于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()一个版本与bool参数作为模板参数。

+0

是不是通过调用bonus()来增加编译时间?还是编译器检测到奖金是空的并且不运行函数调用? – plougue

+1

'bonus ()'调用'bonus'模板的默认版本(示例中的第9行和第10行),所以没有函数调用。换句话说,'MyFunc()'编译成一个代码块(没有条件)和'MyFunc ()'编译成一个不同的代码块(没有条件)。 –

+6

@plougue模板隐式内联,并且内联空函数不会执行任何操作并且可以被编译器取消。 – Yakk

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); 
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(但仅用于调试构建;发布版本不会执行检查)。

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

+0

关于副作用的警告是好的,但是无论使用什么构造,都是如此,无论是模板,if(){...},constexpr等。 – pipe

+0

鉴于OP评论,我自己提高了这一点,因为它是正是他们的最佳解决方案。这就是说,只是一个好奇心:为什么所有的复杂与新的定义和一切,当你可以把doBonusStuff()调用在#if定义(NDEBUG)? – motoDrizzt

+0

@motoDrizzt:如果OP想要在其他函数中做同样的事情,我发现引入一个像这个清理器一样的新宏/更易于读取(和写入)。如果它只是一次性事物,那么我同意直接使用'#if defined(NDEBUG)'可能更容易。 – Cornstalks

8

您可以使用tag分派和简单的函数重载:

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

标签调度是一种广泛使用的通用编程技术,here是一个不错的职位有关的基本知识。