2016-01-05 65 views
1

我想在擁有派生類的實例的對象內獲取基類的模板類型。下面的代碼片段不起作用,因爲Base和它的ArbitraryType不能通過DerivedString被引用。 (標有感嘆號的行)。然而,它絕對可以從它自己的模板類型(OneOfTheDerivedTypes)中推斷出來。在我的情況下,我打算爲AnotherObject使用定義的模板進行繼承,所以我不想只將返回類型硬編碼爲GetSomethingFromThingy()。獲取模板類型的基類內擁有派生實例的類

// ------------------- 
// Provided by my API: 
// ------------------- 
template <typename ArbitraryType> 
class Base { 
    virtual void DoSomething(ArbitraryType); 
}; 

template <typename OneOfTheDerivedTypes> 
class AnotherObject<OneOfTheDerivedTypes> { 
    // Other functions that perform useful tasks are here so that 
    // classes that inherit from AnotherObject need not implement them. 
    void SomethingMagical(); 

    // A function with a specific return type. 
    virtual DerivedString::Base::ArbitraryType GetSomethingFromThingy() = 0; /* ! */ 
protected: 
    OneOfTheDerivedTypes thingy; 
}; 

// -------------------------------------- 
// Someone using my API would make these: 
// -------------------------------------- 
class DerivedFloat : public Base<float> { 
    void DoSomething(float) override; 
}; 

class DerivedString : public Base<string> { 
    void DoSomething(string) override; 
}; 

class UsefulObject : public AnotherObject<DerivedString> { 
    // Knows the required return type of GetSomethingFromThingy() without 
    // needing to specify it as a template. Should throw compile-time error 
    // if you tried to override with a different return type. In other words, 
    // forces return type to be string because of use of DerivedString. 
    string GetSomethingFromThingy() override; 
}; 

一種解決方法是指定稱爲ArbitraryType額外模板ARG如下所示:

template <typename OneOfTheDerivedTypes, typename ArbitraryType> 
class AnotherObject<OneOfTheDerivedTypes> { 
    virtual ArbitraryType GetSomethingFromThingy() = 0; 
protected: 
    OneOfTheDerivedTypes thingy; 
}; 

class UsefulObject<DerivedString, string> : public AnotherObject<DerivedString, string> { 
    string GetSomethingFromThingy() override; 
}; 

程序員然後必須指定參數,其中OneOfTheDerivedTypes要麼DerivedFloat或DerivedString和ArbitraryType被浮子或串, 分別。不是一個好的解決方案,因爲ArbitraryType完全由OneOfTheDerivedTypes的選擇來指定。

我認爲額外的模板(ArbitraryType在AnotherObject)可以通過其基地在公共職能返回ArbitraryType的一個實例來避免(稱之爲ReturnInstanceOfArbitraryType()),並使用decltype(OneOfTheDerivedTypes :: ReturnInstanceOfArbitraryType())AnotherObject內。這看起來很不雅觀,因爲ReturnInstanceOfArbitraryType()在其他情況下是無用的(並且必須是公共的)。這是一個正確的事情是使用特質類嗎?有更好的解決方案嗎? (仍然得到這些新的C++ 11的東西)。謝謝!

+3

裏面'Base'你可以有'的typedef ArbitratyType TypeOfBase'後來在派生類,你可以用'的TypeOfBase'代替'DerivedString :: Base :: ArbitraryType'在你的第一個示例中。 –

+0

啊,是的。這很好,請隨時在下面回答。它堅持認爲我使用'typename DerivedType :: TypeOfBase',這有點不幸。似乎它應該能夠從它被使用的方式以及它是typedef的事實中知道它是一種類型。我在這裏閱讀更多關於它:http://stackoverflow.com/questions/7923369/when-is-the-typename-keyword-necessary – Chet

+0

使用聲明可以幫助清理typename關鍵字:使用TypeOfBase = typename DerivedType: :TypeOfBase – Chet

回答

0

也許我錯誤地理解了你的問題,但是你不能只添加一個typedef到Base

template <typename ArbitraryType> 
class Base { 
    virtual void DoSomething(ArbitraryType); 

    using parameter_type = ArbitraryType; 
}; 

然後你可以參考它:

template <typename OneOfTheDerivedTypes> 
class AnotherObject { 
    // A function with a specific return type. 
    virtual typename OneOfTheDerivedTypes::parameter_type GetSomethingFromThingy() = 0; 
}; 

然後是override派生類型將強制執行的返回類型相同(或協):

class UsefulObject : public AnotherObject<DerivedString> { 
    string GetSomethingFromThingy() override; 
}; 

如果您想要更友好的錯誤消息,您還可以添加static_assert

class UsefulObject : public AnotherObject<DerivedString> { 
    using base_parameter_type = typename AnotherObject<DerivedString>::parameter_type; 
    static_assert(std::is_same<string, base_parameter_type>::value, 
       "mismatched parameter types"); 

    string GetSomethingFromThingy() override; 
}; 

如果您不能修改Base模板,也可以使用某些元編程來檢測類型。首先,聲明(但不限定),其可以從Base<T>推斷類型T的函數:

template<typename T> T* detect_base_parameter_type(Base<T>*); // undefined 

現在定義的別名模板這需要派生類型作爲其模板參數之一,並且使用上面的功能找到它的基類的模板參數:

template<typename DerivedT> 
    using base_parameter_t = typename std::remove_pointer< 
    decltype(detect_base_parameter_type(std::declval<DerivedT*>())) 
    >::type; 

這使用decltype來檢測調用detect_base_parameter_type的指針派生類型的返回類型。該指針將轉換爲指向Base<T>的指針(推導出T用於DerivedT),函數的返回類型將爲T*。然後我們用remove_pointer將它變成T

現在你可以使用別名模板的其他類:

template <typename OneOfTheDerivedTypes> 
class AnotherObject { 
    // A function with a specific return type. 
    virtual base_parameter_t<OneOfTheDerivedTypes> GetSomethingFromThingy() = 0; 
}; 

class UsefulObject : public AnotherObject<DerivedString> { 
    using base_parameter_type = base_parameter_t<DerivedString>; 
    static_assert(std::is_same<string, base_parameter_type>::value, 
       "mismatched parameter types"); 

    string GetSomethingFromThingy() override; 
};