2011-04-13 64 views
4

我想在我的代碼中執行一組類似的測試,但只是根據參數進行更改。使用模板而不是開關

我可以這樣使用switch語句寫:

bool doTest(EnumSensorFamily family, const StructSensorProposal& proposed) 
{ 
    switch (family) 
    { 
    case FAM1: 
    return (ExpectedFam1 == proposed.Fam1SensorId); 
    break; 
    case FAM2: 
    return (ExpectedFam2 == proposed.Fam2SensorId); 
    break; 
    case FAM3: 
    return (ExpectedFam3 == proposed.Fam3SensorId); 
    break; 
    default: 
    ERROR ("Unexpected family"); 
    return false; 
    } 
} 

我想用模板專門做這個的

template <EnumSensorFamily family> 
bool doTest(const StructSensorProposal& proposed); 

template<> 
bool doTest<FAM1> (const StructSensorProposal& proposed) 
{ 
    return (ExpectedFam1 == proposed.Fam1SensorId); 
} 

template<> 
bool doTest<FAM2> (const StructSensorProposal& proposed) 
{ 
    return (ExpectedFam2 == proposed.Fam2SensorId); 
} 

template<> 
bool doTest<FAM3> (const StructSensorProposal& proposed) 
{ 
    return (ExpectedFam3 == proposed.Fam3SensorId); 
} 

有從避免含有switch語句除了這樣做的任何好處幾乎相同的情況?

理想情況下,我希望能夠編寫單一方法來減少維護開銷。

感謝

的方式

回答

6

號樓關閉安德魯的回答......

注意,EnumSensorFamily family必須在編譯時是已知的。如果直到運行時間才知道它,那麼您必須編寫一個switch來選擇模板,並將其返回到您開始的位置。

另一種方式做,這是與特點的模式:

template <EnumSensorFamily family> 
struct SensorTraits; 

template <> 
struct SensorTraits<FAM1> 
{ 
    const EnumSensorFamily kFamilyID = ExpectedFam1; 
}; 

template <> 
struct SensorTraits<FAM2> 
{ 
    const EnumSensorFamily kFamilyID = ExpectedFam2; 
}; 

template <> 
struct SensorTraits<FAM3> 
{ 
    const EnumSensorFamily kFamilyID = ExpectedFam3; 
}; 

template <EnumSensorFamily family> 
bool doTest(const StructSensorProposal& proposed) 
{ 
    return (SensorTraits<family>::kFamilyID == proposed.Fam1SensorId); 
} 

如果您嘗試使用doTest的傳感器家族,缺乏特點的專業化,你會得到一個編譯錯誤。還要注意,你永遠不會實例化一個traits對象,你只需要使用它的定義。

這可以讓你重用常量,typedefs,無論在幾個函數中。此外,添加一個新的家庭不涉及通過所有代碼尋找每個switch聲明關心。你所要做的就是創建一個新的SensorTraits專業化。

編輯:您可以現場取決於傳感器的家庭與一個pointer to member

template <> 
struct SensorTraits<FAM1> 
{ 
    const EnumSensorFamily kFamilyID = ExpectedFam1; 
    int StructSensorProposal::*proposalField = &StructSensorProposal::fam1field; 
}; 

// ... 

template <EnumSensorFamily family> 
int getProposedField(const StructSensorProposal& proposed) 
{ 
    return proposed.*SensorTraits<family>::proposalField; 
} 

你也可以放進去,也就是說,一個typedef爲傳感器的數據類型:

template <> 
struct SensorTraits<FAM1> 
{ 
    const EnumSensorFamily kFamilyID = ExpectedFam1; 
    typedef uint16_t data_type; 
    data_type StructSensorProposal::*proposalField = &StructSensorProposal::fam1field; 
}; 

// ... 

template <EnumSensorFamily family> 
SensorTraits<family>::data_type getProposedField(const StructSensorProposal& proposed) 
{ 
    return proposed.*SensorTraits<family>::proposalField; 
} 

我沒有測試過這些;那裏可能需要conststatic

+0

我可以使用類似的機制來選擇擬議結構中的字段嗎?每個傳感器系列在提案中都有一個單獨的字段。 – DanS 2011-04-13 14:02:58

+0

我在特質內部放置了一個靜態方法,以根據家庭情況返回更正後的傳感器。 – DanS 2011-04-13 15:27:46

+0

我在traits實現中有一個方法:'template <> struct SensorTraits {static EnumSensorId getProposed(const StructSensorProposal&proposed){return proposed.Fam1SensorId;}};'它當然似乎工作。這是否有缺點? – DanS 2011-04-14 09:17:22

0

嗯......你是遠離運行時移動處理編譯時間。編譯器將創建函數並相應地使用它們,而不是在運行時必須通過switch語句。

此外,它會導致更乾淨的代碼。

+0

清潔代碼絕對是一個主觀問題。我個人會發現'switch'更清潔,因爲我不會始終創建和讀取模板源。 (不包括STL的使用) – RedX 2011-04-13 13:43:38

+0

@RedX:正如我指出的那樣,添加傳感器系列意味着找到每個「switch」並添加所需的「case」。 – 2011-04-13 13:54:54

6

如果編譯器無法正確優化switch(即,如果它不生成與模板解決方案相同的代碼,這適用於現代化的內聯編譯器),那麼它的性能提升很小。 當然,只有當family是編譯時常量 - 否則模板不適用,編譯器可以找到的switch的最佳優化是計算跳轉或跳轉表。

如果你的代碼總是看起來像return (ExpectedFamN == proposed.FamNSensorId);,我寧願使用陣列來獲得期望值和傳感器ID,並根據family來索引這些值。

+0

我用這個建議來設置一個期望值的數組,但跟進了Mike的建議以確定傳感器域。謝謝 – DanS 2011-04-13 15:26:55

2

這是不可能的使用在以下情況下,模板:

const EnumSensorFamily familyCompileTime = FAM3; // Compile time constant 
EnumSensorFamily family = GetFimilyInRunTime(); // Run time variable 
doTest1(family, proposed); // ok 
doTest2<family>(proposed); // error; 
doTest2<familyCompileTime >(proposed); // OK; 
+0

是的,它在編譯時已知。感謝您澄清問題。 – DanS 2011-04-13 13:56:42

0

二進制大小也有好處,只有實例化的模板將在可執行文件中。 如果你有一個巨大的開關,它可能會在EXE大小上顯着!

相關問題