2014-09-23 6 views
2

У меня есть два класса C++, SuperClass & SubClass. Каждый из них имеет функцию set, и функция возвращает объект класса в конце, так как я хочу выполнить их в одной строке. Код показан ниже:Возвращение определения подкласса из функции суперкласса

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 
} 

Однако функция Seta() возвращает определение суперкласса, так что я не могу приковать его с функцией SETB(), объявленной в подклассе.

Есть ли способ сделать функцию SetA() возвратом определения SubClass? Так что я могу выполнить их в одной строке.

Заранее спасибо. Elliott

Вопрос Update (на 2014-09-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 в этом случае?

Еще раз спасибо. Elliott

+0

Читайте об [любопытно повторяющемся шаблоне шаблонов] (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). –

+0

мой совет - пересмотреть свой дизайн. CRTP будет работать, но ваш дизайн будет беспорядочным. – egur

+0

Спасибо всем вам.Кажется, CRTP - хороший способ решить эту проблему. Однако могу ли я использовать SuperClass самостоятельно при таком подходе? – Elliott

ответ

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_same и conditional черты по своему усмотрению:

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

Спасибо за ваш оперативный ответ. Однако, могу ли я использовать SuperClass самостоятельно через этот метод CRTP? – Elliott

+0

@Elliott Вы можете, но это связано с некоторыми дополнительными обманами. – 101010

+0

Итак, как ваше предложение? Я думал об использовании функции шаблона в SetA() и SetB() вместо использования CRTP, так что каждый раз, когда вызываются функции, я должен указать тип возвращаемого значения. Но это кажется нехорошим подходом ... :( – Elliott

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

um ... однако я предпочел бы сохранить SuperClass запечатанным при добавлении нового SubClass или новой функции в SubClass. . Этот способ, похоже, не в состоянии сделать это. В любом случае, спасибо за ваш комментарий. :) – Elliott

Смежные вопросы