Почему этот код компилируется?(Вопрос шаблона C ++) - PullRequest
3 голосов
/ 22 января 2011

Я пишу обобщенный контейнер, используя шаблон класса, с ограничением (политикой), что элементы, хранящиеся в контейнере, должны быть получены из определенного базового класса.

Вот определение шаблона класса

// 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. Класс «Данные» НЕ реализует чисто виртуальный метод, объявленный в ABC ContainerItem - с помощью исправлений, рекомендованных в ответах ниже, первая проблема (применение политики) решена, однако компилятор не замечает эти данные не реализуют метод getInvalid () интерфейса ContainerItem. Почему компилятор пропускает эту явную ошибку?

Кстати, детали компилятора и ОС: g ++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3

Ответы [ 2 ]

2 голосов
/ 22 января 2011

Измените IsDerivedFrom2 на IsDerivedFrom, и он не сможет скомпилироваться ожидаемым образом.

Проблема в том, что метод из класса шаблона никогда не создается, если он не вызывается. Изменение имени делает его конструктором, поэтому оно вызывается конструкторами классов, производных от IsDerivedFrom. Он все еще будет компилироваться в пустой код. Компилятор оптимизирует его без мертвых назначений.

Я бы порекомендовал вам не писать код шаблона, подобный этому, если вам удастся использовать Boost , особенно is_base_of из библиотеки признаков типа Boost .

В частности, ваш шаблон 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 голос
/ 22 января 2011

Функция Constraints не генерируется, поскольку на IsDerivedFrom2 никогда не ссылаются. Это обязательное поведение для C ++. Может быть, это помогает вызвать его из конструктора. В противном случае, проверьте библиотеку наддува для функциональности как это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...