2014-09-23 21 views
2

我有兩個C++類,SuperClass &子類。他們每個人都有一個設置功能,該功能將在最後返回類對象本身,因爲我想在一行中執行它們。代碼如下所示:從超類功能返回子類定義

class SuperClass 
{ 
public: 
    SuperClass& SetA(int a) 
    { 
     m_a = a; 
     return *this; 
    } 
    virtual void Print() 
    { 
     printf("a=%i", m_a); 
    } 
protected: 
    int m_a; 
}; 
class SubClass : public SuperClass 
{ 
public: 
    SubClass& SetB(double b) 
    { 
     m_b = b; 
     return *this; 
    } 
    virtual void Print() 
    { 
     printf("a=%i, b=%f", m_a, m_b); 
    } 
protected: 
    double m_b; 
}; 

int main(int argc, char* argv[]) 
{ 
    SubClass().SetB(123.4).SetA(123).Print();  // Works fine 
    SubClass().SetA(123).SetB(123.4).Print();  // Failed 
} 

然而,組A()函數返回超類的定義,所以我不能在子類中聲明的組B()函數鏈它。

有沒有什麼辦法讓SetA()函數返回子類的定義?這樣我就可以在一行中執行它們。

在此先感謝。 埃利奧特

問題更新(在2014年9月24日)爲CRTP的方法: 感謝所有意見。我認爲CRTP是解決這個問題的好方法。不過,我也想單獨使用SuperClass。說:

int main(int argc, char* argv[]) 
{ 
    SubClass().SetB(123.4).SetA(123).Print();  // Works fine 
    SubClass().SetA(123).SetB(123.4).Print();  // Failed, but works in CRTP 
    SuperClass().SetA(123).Print();     // Is CRTP able to do this? 
} 

在這種情況下,CRTP方法是否也有幫助?

再次提前致謝。 埃利奧特

+0

閱讀[奇怪的循環模板模式](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)。 – 2014-09-23 11:56:46

+0

我的建議是重新考慮你的設計。 CRTP可以工作,但是你的設計會很混亂。 – egur 2014-09-23 13:19:07

+0

謝謝大家的意見。看來CRTP是解決這個問題的好方法。但是,我可以在這種方法下單獨使用SuperClass嗎? – Elliott 2014-09-24 01:44:08

回答

2

看來CRTP是解決這個的好辦法。但是,我可以在這種方法下單獨使用SuperClass嗎?

DEMO

#include <type_traits> 

// make it a template 
template <typename T = void> 
class SuperClassCRTP 
{ 
    // determine what type SetA (and others) should return 
    using CRTP = typename std::conditional<std::is_same<T, void>::value 
              , SuperClassCRTP 
              , T>::type; 

public: 
    // return CRTP& instead: 
    CRTP& SetA(int a) 
    { 
     m_a = a; 
     return static_cast<CRTP&>(*this); // cast the *this to desired type 
    } 

    virtual void Print() 
    { 
     printf("a=%i", m_a); 
    } 
protected: 
    int m_a; 
}; 

class SubClass : public SuperClassCRTP<SubClass> 
//    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ 
//    specialize the base class with self type 
{ 
public: 
    SubClass& SetB(double b) 
    { 
     m_b = b; 
     return *this; 
    } 
    virtual void Print() 
    { 
     printf("a=%i, b=%f", m_a, m_b); 
    } 
protected: 
    double m_b; 
}; 

// define an alias 
using SuperClass = SuperClassCRTP<>; 

int main() 
{ 
    SubClass().SetB(123.4).SetA(123).Print(); 
    SubClass().SetA(123).SetB(123.4).Print(); 
    SuperClass().SetA(123).Print(); 
} 

我使用VC2005,有什麼方法嗎?

你可以寫上自己的is_sameconditional性狀:

DEMO

template <bool b, typename T, typename F> 
struct conditional 
{ 
    typedef F type; 
}; 

template <typename T, typename F> 
struct conditional<true, T, F> 
{ 
    typedef T type; 
}; 

template <typename T, typename U> 
struct is_same 
{ 
    static const bool value = false; 
}; 

template <typename T> 
struct is_same<T, T> 
{ 
    static const bool value = true; 
}; 

template <typename T = void> 
class SuperClassCRTP 
{ 
    typedef typename conditional<is_same<T, void>::value 
           , SuperClassCRTP 
           , T>::type CRTP; 

// ... 

typedef SuperClassCRTP<> SuperClass; 

int main() 
{ 
    SubClass().SetB(123.4).SetA(123).Print(); 
    SubClass().SetA(123).SetB(123.4).Print(); 
    SuperClass().SetA(123).Print();  
} 
2

您可以使用curiously recurring template pattern如下面的例子:

template<class T> 
class SuperClass { 
public: 
    T& SetA(int a) { 
     m_a = a; 
     return *dynamic_cast<T*>(this); 
    } 
    virtual void Print() { 
     printf("a=%i", m_a); 
    } 
protected: 
    int m_a; 
}; 
class SubClass : public SuperClass<SubClass> 
{ 
public: 
    SubClass& SetB(double b) { 
     m_b = b; 
     return *this; 
    } 
    virtual void Print() { 
     printf("a=%i, b=%f", m_a, m_b); 
    } 
protected: 
    double m_b; 
}; 

LIVE DEMO

+0

感謝您的及時答覆。但是,我可以通過這種CRTP方法單獨使用SuperClass嗎? – Elliott 2014-09-24 01:32:32

+0

@Elliott你可以但這需要一些額外的欺騙。 – 101010 2014-09-24 06:00:56

+0

那麼你的建議如何?我正在考慮在SetA()和SetB()上使用模板函數,而不是使用CRTP,所以每次調用函數時都必須指定返回類型。但這似乎不是一個好辦法... :( – Elliott 2014-09-24 06:09:17

2

除了使用編譯時多態性的解決方案,您還可以使用具有虛函數的運行時多態性實現類似的結果。爲此,您需要在派生類中聲明所有派生類中使用的函數virtual(可能帶有虛擬實現),然後在派生類中重寫它們。

#include <cstdio> 

class SuperClass { 
public: 
    SuperClass &SetA(int a) { 
    m_a = a; 
    return *this; 
    } 
    virtual void Print() { printf("a=%i", m_a); } 
    virtual SuperClass &SetB(double b) { return *this; }; 

protected: 
    int m_a; 
}; 

class SubClass : public SuperClass { 
public: 
    SubClass &SetB(double b) { 
    m_b = b; 
    return *this; 
    } 
    virtual void Print() { printf("a=%i, b=%f", m_a, m_b); } 

protected: 
    double m_b; 
}; 

int main(int argc, char *argv[]) { 
    SubClass().SetB(123.4).SetA(123).Print(); 
    printf("\n"); 
    SubClass().SetA(123).SetB(123.4).Print(); 
} 
+0

恩......不過,我寧願在SubClass中添加一個新的SubClass或一個新的函數時保持SuperClass密封。這種方式似乎無法做到。無論如何,非常感謝您的評論。 :) – Elliott 2014-09-24 01:39:13