2011-08-25 35 views
7

我有下面的代碼不能編譯。帶接口對象的C++模板

class Base { 
    public: 
     virtual ~Base() { }; 
}; 

class Derived : public Base { }; 

class NotDerived { }; 

template <typename T> 
class Group { }; 

int main() { 
    Group<Base> *g = NULL; 

    g = new Group<Base>();  // Works 
    g = new Group<Derived>(); // Error, but I want it to work 

    g = new Group<NotDerived>(); // Error, as expected 
} 

我明白,這不能編譯,因爲g是不同的類型Group<Derived>。爲了在Java中完成這項工作,我會做一些事情,例如Group<? extends Base> g,但據我所知C++沒有那個關鍵字。可以做什麼?

編輯:我想澄清一下,我不希望它有可能將Base的類型設置爲g。我已經更新了我的例子來解釋這一點。

編輯2:我的問題有兩種解決方案。 Dave's我發現很簡單,很容易定義。但Bowie's(加上Mark's)更適合我的需求。

+0

我不熟悉Java是如何工作的。你想用這種語法說什麼? –

+0

這在Java中也不起作用。 '<?的語法擴展...>不是爲了支持協方差,而是在那裏爲泛型可能使用的類型添加約束。 –

+0

請參閱[C++模板多態性](http://stackoverflow.com/questions/2203388/c-templates-polymorphism) – Praetorian

回答

3

你可以讓集團<數據庫>基類的所有<集團T> T!=基地。

class Base { 
    public: 
     virtual ~Base() { }; 
}; 

class Derived : public Base { }; 


template <typename T> class Group; 

struct Empty { }; 

template <typename T> 
struct base_for_group_t { 
    typedef Group<Base> type; 
}; 

template <> 
struct base_for_group_t<Base> { 
    typedef Empty type; 
}; 

template <typename T> 
class Group : public base_for_group_t<T>::type { }; 

int main() { 
    Group<Base> *g = 0; 

    g = new Group<Base>(); // Works 
    g = new Group<Derived>(); // now works 
} 
+0

不幸的是,現在像這樣的東西是合法的:'class NotDerived {}; g =新組();'。如果可能,我想保持類型安全。 – Ryan

1

我不認爲C++支持。 C++ Template在編譯時完全處理,因此它不支持多態性。這意味着模板參數的類型應該對賦值表達式的兩側完全相同。

5

的類Group<Base>Group<Derived>是完全不相關的,不同的類。指向他們的方向不可轉換。

如果你需要運行時多態性的行爲,你的類模板Group可以從普通(非模板)基類派生:

class Group // base 
{ 
    virtual ~Group() { } 
}; 

template <typename T> 
class ConcreteGroup : public Group 
{ 
    // ... 
    T * m_impl; 
}; 

Group * g1 = new ConcreteGroup<A>; 
Group * g1 = new ConcreteGroup<B>; 
+0

ConcreteGroup不需要繼承Group? –

+0

@穆:是的,謝謝,修好! –

2

Bowie Owens's Answer處理您需要解決原始問題的協方差。至於你在編輯問題中所要求的限制 - 你可以通過使用類型特徵來實現。

template <typename T, class Enable = void> class Group; 

template <typename T> 
class Group<T, typename enable_if<is_base_of<Base, T>::value>::type> 
    : public base_for_group_t<T>::type { }; 
+0

不幸的是,我無法得到它在我的最終編譯(我不熟悉如何正確使用類型特徵)。你能提供一個更完整的例子嗎? (我認爲'Group '應該是'template class Group',但是這不起作用。 – Ryan

+0

再次用':: value 'on is_base_of。(boost :: enable_if'和'std :: enable_if'之間的區別)如果不是,你得到的錯誤是什麼? –

+0

啊,我沒有包含std命名空間,你原來的代碼按預期工作。 ;) – Ryan

0

我想我明白你要做什麼。我不確定這是最好的方法(你可能想看看Boost.Factory)。

template <class T> 
class Factory { 

public: 
    virtual T* operator()() = 0; 
}; 

template <class Derived, class Base> 
class ConcreteFactory : public Factory<Base> { 

public: 
    virtual Base* operator()() { 
    return new Derived(); 
    } 
}; 

class Base { 
public: 
    virtual ~Base() {}; 
}; 

class Derived1 : public Base { }; 
class Derived2: public Base {}; 

class NotDerived {}; 

int main() 
{ 
    Factory<Base>* g; 

    g = new ConcreteFactory<Derived1, Base>; 
    g = new ConcreteFactory<Derived2, Base>; 
    // g = new ConcreteFactory<NotDerived, Base>; // Will not work if you try to do this 
} 
+0

這似乎是迄今爲止最安全的解決方案。 Derived1和Base的定義有點多餘,但這不是我已經不知道的(我可以用typedef來避免)。謝謝, :) – Ryan