2017-07-03 54 views
1

對於T類型的對象,我有一堆有用的函數。這裏T需要爲功能提供一些界面來處理它。有幾種常見的接口實現。所以我讓他們使用CRTP作爲mixin工作。防止意外隱藏(由CRTP mixin提供的方法)

template<class T> 
struct InterfaceImpl { 
    using ImplType = InterfaceImpl<T>; 
    int foo(); 
    ... 
}; 

struct MyData : public InterfaceImpl<MyData> { 
    ... 
}; 

template<class T> 
void aUsefulFunction(T& t) { 
    //Working with `t`. 
    //This cast is to workaround an accidental hiding of `foo` by MyData. 
    static_cast<T::ImplType&>(t).foo(); 
} 

我想提供的實施InterfaceImpl(和其他的實現也),因爲它是有些道理的。隱藏他們的一些方法可能是非常危險的。他們有什麼辦法來強制孩子班級沒有壓倒一切?我在類似的問題上閱讀了link,但討論並未給出令人滿意的解決方案。如果沒有合理的方法,我期望在上面的代碼中的投射可以提供一些安全性。還是有其他解決方案來解決這個問題嗎?

+0

您無法覆蓋非虛擬方法(可以將其隱藏在派生類中)。避免調用任何此類方法的唯一方法是接受基類實例或(如您所做的)將其轉換爲基類的類型。我將簡化上面的內容,並簡單地接受該方法中的任何'InterfaceImpl '而不是任何'T'。 – Nim

+0

@Nim有幾種不同的接口實現,我想避免運行時分派。因此指定參數類型是不可能的。 – jjdoe

+0

順便說一句 - 我認爲這個問題有你需要的答案:https://stackoverflow.com/questions/4465686/how-to-prevent-a-method-from-being-overridden-in-derived-class,我會投票作爲副本關閉。如果不清楚,這是第二個答案(標記'虛擬'和'最終'的方法 - 如果任何人在派生類中實現該方法,這將導致編譯器錯誤。) – Nim

回答

2

您可以創建一個特徵,看是否Tfoo和使用上static_assert

typename <typename T, typename ...Ts> 
using foo_type = decltype(std::declval<T>().foo(std::declval<Ts>()...)); 

template <typename T> 
using has_foo = is_detected<foo_type, T>; 

template<class T> 
struct InterfaceImpl { 
    static_assert(!has_foo<T>::value, "T should not have foo method"); 

    using ImplType = InterfaceImpl<T>; 
    int foo(); 
}; 

MyData仍然可以隱藏fooMyData::foo(int)或相似,但你會代替如果調用錯誤的方法編譯錯誤。

+0

出於好奇,「虛擬」和「最終」沒有達到相同的效果*而不需要上述的機器? – Nim

+0

@Nim:不完全是,'virtual void InterfaceImpl :: foo()final'可以與'void MyData :: foo()const'或'void MyData :: foo(int = 42)'共存。另外,多態性具有(運行時間)成本,而我們只想在編譯時檢查某些內容。 – Jarod42

+0

除非我誤解了(我可能會這麼做),'virtual'' final'技巧會捕獲相同的情況(至少在我的編譯器中,如果你重載了一個虛擬方法,它會產生一個警告......)被任何情況所困住。現在,就運行時成本而言,我想可能會有,但我可以想象,在我們所看到的情況下,編譯器可以足夠聰明地認識到它並不真正調用虛擬函數,並且執行正確東西(如內聯,如果可能..?)可能是我希望太多.. :) – Nim