2011-01-22 88 views
3

我正在寫一個使用類模板的通用容器,並且有一個限制(策略),容器中存儲的項目應該從特定的基類派生。這段代碼爲什麼編譯? (C++模板問題)

這裏是類模板

// GenericContainer.hpp 
// -------------------------------------- 
class ContainerItem 
{ 
protected: 
    virtual ContainerItem& getInvalid() = 0; 

public: 
    virtual ~ContainerItem(); 
    bool isValid() const; 
}; 


template<typename D, typename B> 
class IsDerivedFrom 
{ 
    static void Constraints(D* p) 
    { 
     B* pb = p; // this line only works if 'D' inherits 'B' 
     pb = p; // suppress warnings about unused variables 
    } 

protected: 
    void IsDerivedFrom2() { void(*p)(D*) = Constraints; } 
}; 


// Force it to fail in the case where B is void 
template<typename D> 
class IsDerivedFrom<D, void> 
{ 
    void IsDerivedFrom2() { char* p = (int*)0; /* error */ } 
}; 



template <class T> 
class GenericContainer : public IsDerivedFrom<T, ContainerItem> 
{ 
private: 
    typedef std::vector<T> TypeVect; 
    void addElement(const T& elem); 

    TypeVect m_elems; 

public: 
    unsigned int size() const; 
    T& elementAt(const unsigned int pos); 
    const T& elementAt(const unsigned int pos) const; 
}; 


template <class T> 
void GenericContainer<T>::addElement(const T& elem) 
{ 
    m_elems.push_back(elem); 
} 

template <class T> 
unsigned int GenericContainer<T>::size() const 
{ 
    return m_elems.size(); 
} 

template <class T> 
T& GenericContainer<T>::elementAt(const unsigned int pos) 
{ 
    unsigned int maxpos = m_elems.size(); 
    if (pos < maxpos) 
     return m_elems[pos]; 
    return T::getInvalid(); 
} 


template <class T> 
const T& GenericContainer<T>::elementAt(const unsigned int pos) const 
{ 
    unsigned int maxpos = m_elems.size(); 
    if (pos < maxpos) 
     return m_elems[pos]; 
    return T::getInvalid(); 
} 


// Class to be contained (PURPOSELY, does not derive from ContainerItem) 
// Data.hpp 
//---------------------------------------------------------------- 

class Data 
{ /* implem details */}; 


// Container for Data items 
// Dataset.h 
// ---------------------------------------------------------------------------- 

#include "GenericContainer.hpp" 
#include "Data.hpp" 

class Dataset: public GenericContainer<Data> 
{ 
public: 
    Data& getInvalid(); 
}; 


// C++ source 
// ----------------------------------------------------------- 
#include "Dataset.hpp" 

Dataset ds; 

任何人都可以解釋爲什麼上面的代碼編譯的定義?

[編輯]

上面的代碼不應該有兩個原因編譯:

  1. 類「數據」不從ContainerItem導出,但它可以被存儲在GenericContainer(如類Dataset所示)。順便說一下,這個問題現在已經解決了,這要歸功於Omifarious和jdv給出的答案

  2. 類'Data'不實現在ABC ContainerItem中聲明的純虛方法 - 使用下面答案中推薦的修復,第一個問題(策略執行)已解決,但編譯器未能注意到Data沒有實現ContainerItem'接口'的getInvalid()方法。爲什麼編譯器錯過了這個明顯的錯誤?

順便說一句,編譯器和OS的細節: 克++(Ubuntu的4.4.3-4ubuntu5)4.4.3

+1

如果你解釋爲什麼它不應該在你的意見中編譯,這將有助於我們理解你的問題。 – 2011-01-22 09:36:18

+0

這個問題不應該因爲它太多的代碼讓你閱讀或者你不理解而關閉。這個人犯了一個有趣的錯誤,這個問題值得回答。 – Omnifarious 2011-01-22 09:49:09

回答

2

變化IsDerivedFrom2IsDerivedFrom和它無法編譯在剛剛預期的方式。

問題是如果未調用模板類中的方法,則永遠不會實例化該方法。更改名稱使其成爲構造函數,所以它最終被來自IsDerivedFrom的派生類構造函數調用。它仍然會編譯爲空代碼。編譯器將優化它去掉分配。

如果你能設法使用Boost,特別是is_base_of from the Boost type traits library,我會建議你不要這樣寫自己的模板代碼。

特別是,你GenericContainer模板可以更簡單和容易實現的使用Boost這樣:

#include <boost/static_assert.hpp> 
#include <boost/type_traits/is_base_of.hpp> 

template <class T> 
class GenericContainer 
{ 
private: 
    typedef std::vector<T> TypeVect; 
    void addElement(const T& elem); 

    TypeVect m_elems; 

public: 
    unsigned int size() const; 
    T& elementAt(const unsigned int pos); 
    const T& elementAt(const unsigned int pos) const; 

    GenericContainer() { 
     BOOST_STATIC_ASSERT((::boost::is_base_of<ContainerItem, T>::value)); 
    } 
}; 


template <class T> 
void GenericContainer<T>::addElement(const T& elem) 
{ 
    m_elems.push_back(elem); 
} 

template <class T> 
unsigned int GenericContainer<T>::size() const 
{ 
    return m_elems.size(); 
} 

template <class T> 
T& GenericContainer<T>::elementAt(const unsigned int pos) 
{ 
    unsigned int maxpos = m_elems.size(); 
    if (pos < maxpos) 
     return m_elems[pos]; 
    return T::getInvalid(); 
} 


template <class T> 
const T& GenericContainer<T>::elementAt(const unsigned int pos) const 
{ 
    unsigned int maxpos = m_elems.size(); 
    if (pos < maxpos) 
     return m_elems[pos]; 
    return T::getInvalid(); 
} 
1

,不會產生Constraints功能,因爲IsDerivedFrom2不會被引用。這是C++的必需行爲。也許它有助於從構造函數中調用它。否則,檢查boost庫是否有這樣的功能。