开发者

Why does this code compile? (C++ template question)

开发者 https://www.devze.com 2023-02-06 16:45 出处:网络
I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.

I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.

Here is the definition of the class template

// 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;

Can anyone explain why the code above compiles?.

[Edit]

The code above should NOT compile for two reasons:

  1. The class 'Data' does NOT derive from ContainerItem, and yet it can be stored in GenericContainer (as illustrated by the class Dataset). Incidentally, this issue has now been resolved than开发者_C百科ks to the answer given by Omifarious and jdv

  2. The class 'Data' does NOT implement the pure virtual method declared in the ABC ContainerItem - using the fixes recommended in the answers below, the first issue (enforcement of policy) is resolved, however the compiler fails to notice that Data does not implement the getInvalid() method of the ContainerItem 'interface'. Why is the compiler missing this glaring mistake?

BTW, compiler and OS details are: g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3


Change IsDerivedFrom2 to IsDerivedFrom and it fails to compile in just the expected manner.

The problem is that a method from a template class is never instantiated if it isn't called. Changing the name makes it a constructor, so it then ends up being called by the constructors of classes derived from IsDerivedFrom. It will still compile to empty code. The compiler will optimize it away the dead assignment.

I would recommend you not write template code like this yourself if you can manage to use Boost, particularly is_base_of from the Boost type traits library.

In particular, your GenericContainer template can be more simply and easily implemented this way using 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();
}


The Constraints function is not generated because IsDerivedFrom2 is never referenced. This is required behavior for C++. Maybe it helps to call it from the constructor. Otherwise, check the boost library for functionality like this.

0

精彩评论

暂无评论...
验证码 换一张
取 消