C++ 用继承减少模板膨胀

C++ 用继承减少模板膨胀,c++,inheritance,templates,code-generation,C++,Inheritance,Templates,Code Generation,有没有人有通过继承减少模板代码膨胀的经验 我不太愿意这样重写我们的容器: class vectorBase { public: int size(); void clear(); int m_size; void *m_rawData; //.... }; template< typename T > class vector : public vectorBase { void push_back( const T&

有没有人有通过继承减少模板代码膨胀的经验

我不太愿意这样重写我们的容器:

class vectorBase
{
  public:
    int size();
    void clear();
    int m_size;
    void *m_rawData;
    //....
};

template< typename T > 
class vector : public vectorBase
{
    void push_back( const T& );
    //...

};
类向量库
{
公众:
int size();
无效清除();
国际货币单位大小;
void*m_原始数据;
//....
};
模板
类向量:公共向量库
{
无效推回(常数T&);
//...
};

我应该在减少编译时间的同时保持最高性能。我还想知道为什么标准库实现不使用这种方法。

我认为这是一种过早的优化。一般来说,除了在嵌入式系统中,磁盘空间和内存都是丰富而廉价的,因此没有理由尝试为少量的代码空间进行优化。通过将其全部保存在模板代码中,可以更清楚地了解发生了什么,而不是使用继承,这会使事情变得复杂

此外,大多数应用程序不会生成成百上千的实例化,并且对于每个实例,并不是所有的方法都可以使用,从而进一步减少了代码占用

<>只有当有非常严密的内存考虑(嵌入)时,我才会考虑不同的可能的方法(包括你提出的方法)。

编辑:我不确定在一些标准的容器案例中会有多少好处,因为它们仍然需要大量的模板代码。对于只有少量特定于模板的代码和大量公共逻辑的内部类,这无疑有助于生成代码和提高编译速度。我怀疑它不常被使用,因为它更复杂,而且好处仅限于某些场景。

IIRC,Qt使用(或使用?)类似的方法用于他们的QList等人

基本上,这是可行的,但您必须确保将依赖于
T
的所有内容都放在向量模板中。不幸的是,这几乎是vector类中的所有代码(在许多情况下,代码需要调用一些
T
构造函数或析构函数),除了分配原始存储和
size()
/
容量()。我不确定它是否有回报,所以请仔细检查


当您可以从一些模板参数中抽象出来(例如,
set::iterator
不需要知道集合的比较器),或者如果您可以为一大类类型(例如,使用简单的复制ctor和dtor)编写一个完整的实现,那么这当然是值得的。

您发布的代码完全是错误的。如果存储在向量中的类具有析构函数,则不会调用该析构函数,因为编译器
vectorBase
通过强制转换为
void*
已丢失有关何时调用析构函数的所有信息

为了正确地执行此操作,调用右析构函数,您需要生成每个调用右析构函数的代码的不同副本,使用模板可以简化此工作

(使用非模板基类的方法,需要生成同样多的机器代码,但需要手工编写更多的C++代码)


这就是为什么这种方法不值得的原因。

如果您不知道存储的元素是什么类型的,那么对向量进行的操作就很少有意义。例如,添加到基类中的
clear()
方法需要调用从向量中移除的元素的析构函数,因此它需要知道它们的类型并需要模板化

在不知道其中的内容类型的情况下,对a
void*m_rawData
执行的操作也确实不多,基本上,它上的所有操作至少都必须知道存储类型的大小。我能想到的唯一一件事是,如果您知道它不包含任何元素(如果它包含必须调用其析构函数的元素),则可以
free()
it。如果您不知道各个元素的起点和终点,那么分配、设置和访问这些元素都不起作用。此外,如果
m_rawData
是一个正确类型的
T*
,那么所有方法的实现都会更加清晰和简单

基类中的
size()。您可能会实现is,以便显式存储大小,但同样地,
size()
可能不是一个需要很长时间编译的方法,即使它是模板化的

总之,我不认为还有很多方法可以在基类中实现。向量上的大多数操作都需要知道存储在向量中的元素。

简称:

是的,这种方法[可能]在有限的、专门的情况下有效。我不怀疑
std::vector
(或STL的其他部分)会出现这种情况

它的长:

正如其他人所提到的(我也同意),在嵌入式系统之外,在已编译的二进制文件上,代码膨胀不是什么大问题

但是,我们中的许多人在编译步骤中构建的代码数量要比有编译库链接(而不是编译头文件)时要多,因此要承受编译成本。再加上更改其中一个模板化头文件的困难,并观看整个项目从头开始重新编译。编译时间过长会让开发人员感到悲伤:(

它可能不会影响大部分开发人员,这取决于贵公司代码库的大小以及构建环境的结构。这当然会对我们公司产生影响

正如一些答案所指出的那样,从
std::vector
中抽象出来的东西并不多,在您的示例中,这将使它变得更公平。当然,您需要能够创建和销毁单个元素,并且使任何方法
虚拟化将阻碍运行时性能(这一点更为重要)
  template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class vector : protected _Vector_base<_Tp, _Alloc>
    {
    ... 
    }