为什么这段代码可以编译? (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;
任何人都可以解释为什么上面的代码可以编译吗?
[编辑]
上面的代码不应编译,原因有两个:
“Data”类不是从 ContainerItem 派生的,但它可以存储在 GenericContainer 中(如 Dataset 类所示) 。顺便说一句,由于 Omifrious 和 jdv 给出的答案,这个问题现在已经得到解决。
类“Data”没有实现 ABC ContainerItem 中声明的纯虚拟方法 - 使用下面的答案中建议的修复,第一个问题(策略的执行)已解决,但是编译器没有注意到 Data 没有实现 ContainerItem“接口”的 getInvalid() 方法。为什么编译器会忽略这个明显的错误?
顺便说一句,编译器和操作系统详细信息是: g++(Ubuntu 4.4.3-4ubuntu5)4.4.3
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:
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 thanks to the answer given by Omifarious and jdv
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
将
IsDerivedFrom2
更改为IsDerivedFrom
并且它无法以预期的方式编译。问题是模板类中的方法如果不被调用就永远不会被实例化。更改名称使其成为构造函数,因此最终会被从
IsDerivedFrom
派生的类的构造函数调用。它仍然会编译为空代码。编译器将对其进行优化以消除死分配。如果您可以设法使用 Boost,特别是
is_base_of
类型特征库。特别是,您的
GenericContainer
模板可以使用 Boost 以这种方式更加简单、轻松地实现:Change
IsDerivedFrom2
toIsDerivedFrom
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:不会生成
Constraints
函数,因为从未引用IsDerivedFrom2
。这是 C++ 所需的行为。也许从构造函数中调用它会有所帮助。否则,请检查 boost 库是否有类似的功能。The
Constraints
function is not generated becauseIsDerivedFrom2
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.