为什么这段代码会编译? (C ++模板问题)

时间:2011-01-22 09:33:18

标签: c++ templates

我正在使用类模板编写一个通用容器,其中包含一个限制(策略),即容器中存储的项应该来自特定的基类。

这是类模板的定义

// 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. 类'Data'不是从ContainerItem派生的,但它可以存储在GenericContainer中(如Dataset类所示)。顺便提一下,由于Omifarious和jdv给出了答案,这个问题现在已经解决了

  2. 类'Data'不实现在ABC ContainerItem中声明的纯虚方法 - 使用下面答案中建议的修复,解决了第一个问题(策略的实施),但是编译器没有注意到Data没有实现ContainerItem'接口'的getInvalid()方法。为什么编译器错过了这个明显的错误?

  3. BTW,编译器和操作系统的详细信息是: g ++(Ubuntu 4.4.3-4ubuntu5)4.4.3

2 个答案:

答案 0 :(得分:2)

IsDerivedFrom2更改为IsDerivedFrom,但无法按预期方式进行编译。

问题是如果未调用模板类中的方法,则永远不会实例化该方法。更改名称使其成为构造函数,因此最终由IsDerivedFrom派生的类的构造函数调用。它仍将编译为空代码。编译器会优化掉死分配。

如果您可以设法使用Boost,我建议您不要自己编写这样的模板代码,尤其是is_base_of from the Boost type traits library

特别是,使用Boost可以更简单,更轻松地实现您的GenericContainer模板:

#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 :(得分:1)

未生成Constraints函数,因为永远不会引用IsDerivedFrom2。这是C ++的必需行为。也许从构造函数中调用它可能会有所帮助。否则,请检查boost库以获取此类功能。