2011-01-22 169 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库是否有这样的功能。