2017-04-25 241 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是一個不錯的職位有關的基本知識。