@[TOC]模板与继承
命名模板参数
命名模板参数主要利用了C++继承中的一个规则,子类重定义,会隐藏基类中定义:
class DefaultPolicy1
{
public:
void print()
{
cout << "Policy 1" << endl;
}
};
class DefaultPolicy2
{
public:
void print()
{
cout << "Policy 2" << endl;
}
};
class DefaultPolicies
{
public:
typedef DefaultPolicy1 P1;
typedef DefaultPolicy2 P2;
};
class DefaultPolicyArgs : virtual public DefaultPolicies
{
};
class Policy1_is : virtual public DefaultPolicies
{
public:
typedef DefaultPolicy1 P2;
};
class Policy2_is : virtual public DefaultPolicies
{
public:
typedef DefaultPolicy2 P1;
};
class PolicySelector : public DefaultPolicyArgs, public Policy2_is
{
};
int main()
{
PolicySelector::P1 p;
p.print();
}
上面例子中,Policy2_is重定义了P1,所以在PolicySelector中P1是DefaultPolicy2,结果输出:Policy 2。
DefaultPolicyArgs 类似乎是毫无意义的。如果删除DefaultPolicyArgs定义,PolicySelector定义改为:
class PolicySelector : public DefaultPolicies, public Policy2_is
{
};
编译器会提示PolicySelector::P1不明确。
在上面例子中加入模板:
class DefaultPolicy1
{
};
class DefaultPolicy2
{
};
class DefaultPolicy3
{
};
class DefaultPolicy4
{
};
class SpecialPolicy
{
};
class DefaultPolicies
{
public:
typedef DefaultPolicy1 P1;
typedef DefaultPolicy2 P2;
typedef DefaultPolicy3 P4;
typedef DefaultPolicy3 P4;
};
class DefaultPolicyArgs : virtual public DefaultPolicies
{
};
template <typename Policy>
class Policy_is1 : virtual public DefaultPolicies
{
public:
typedef Policy P1;
};
template <typename Policy>
class Policy_is2 : virtual public DefaultPolicies
{
public:
typedef Policy P2;
};
template <typename Policy>
class Policy_is3 : virtual public DefaultPolicies
{
public:
typedef Policy P3;
};
template <typename Policy>
class Policy_is4 : virtual public DefaultPolicies
{
public:
typedef Policy P4;
};
typename <typename Base, int D>
class Discriminator : public Base
{
};
template <typename Setter1, typename Setter2, typename Setter3, typename Setter4>
class PolicySelector : public Discriminator<Setter1, 1>,
public Discriminator<Setter2, 2>,
public Discriminator<Setter3, 3>,
public Discriminator<Setter4, 4>
{
};
template <typename PolicySetter1 = DefaultPolicyArgs,
typename PolicySetter2 = DefaultPolicyArgs,
typename PolicySetter3 = DefaultPolicyArgs,
typename PolicySetter4 = DefaultPolicyArgs>
class BreadSlicer
{
public:
typedef PolicySelector<PolicySetter1, PolicySetter2, PolicySetter3, PolicySetter4> Policies;
};
现在,使用BreadSlicer可以直接指定任何一个模板实参。如果定义一个BreadSlicer<Policy3_is >变量,那么PolicySelector相当于继承了DefaultPolicyArgs和Policy3_is,所以P3被重定义为SpecialPolicy类。模板Discriminator的引用是为了避免继承同一个类。
空基类优化EBCO
class EmptyClass
{
};
一般地,定义了一个EmptyClass这样的空类,其对象的大小也不会为0,也就是说需要分配内存。
但是C++规定,当空类作为基类时,只要不会与同一类型的另一个对象或者子对象分配在同一地址,就不需要为其分配内存。这就是空基类优化。
class Empty
{
};
class EmptyToo : public Empty
{
};
class EmptyThree : public EmptyToo
{
};
在支持空基类优化的编译器环境中,上面代码中的3个类大小相同,但都不为0。也就是说EmptyToo和EmptyThree的基类都不占内存。C++环境中不存在真正意义上不占内存的类,空基类优化是针对空类作为基类时,在子类中是否需要分配内存的一个规则。
class NonEmpty : public Empty, public EmptyToo
{
};
NonEmpty类中的基类Empty和EmptyToo都是占内存。否则,NonEmpty中的Empty直接基类和EmptyToo中的Empty间接基类地址就会重合,这违反了空基类优化规则。
下面是空基类优化的一个应用例子(更多情况可百度boost.compressed_pair):
template <typename CustomClass>
class Optimizable
{
private:
CustomClass info;
void* storage;
};
其中CustomClass一定是类类型,且可能为空,那么可以优化:
template <typename CustomClass>
class Optimizable
{
private:
BaseMemberPair<CustomClass, void*> info_and_storage;
};
template <typename Base, typename Member>
class BaseMemberPair : private Base
{
private:
Member member;
public:
BaseMemberPair(Base const &b, Member const &m) : Base(b), member(m)
{}
Base const& first() const
{
return (Base const&)*this;
}
Base& first()
{
return (Base&)*this;
}
Member const& second() const
{
return this->member;
}
Member& second()
{
return this=>member;
}
};
奇特的递归模板模式CRTP
CRTP的主要特点是派生类将自身作为模板参数传给基类。
下面例子是一个数某个类对象个数的类模板。当需要计算某个类对象个数时,只要继承ObjectCount模板就可以,模板实参自然是类本身:
template <typename CountedType>
class ObjectCounter
{
private:
static size_t count;
protected:
ObjectCounter()
{
++ObjectCounter<CountedType>::count;
}
ObjectCounter(ObjectCounter<CountedType> const&)
{
++ObjectCounter<CountedType>::count;
}
~ObjectCounter()
{
--ObjectCounter<CountedType>::count;
}
public:
static size_t live()
{
return ObjectCounter<CountedType>::count;
}
};
template <typename CountedType>
size ObjectCounter<CountedType>::count = 0;
CRTP把类的功能提取出来,放到一个依赖于自身的模板类中实现,再继承模板类重新获取该功能。如果只有一个类需要,或者只有一个类通过该模式能够实现功能,那该模式是没有意义。CRTP的本质是为了泛化,所以为了不失一般性,一般只提取仅能用作成员函数的接口(构造函数、析构函数、下标运算符Operator[]等)的功能。换句话说,如果有一个功能是在仅能用作成员函数的接口中实现的,并且意图把功能应用到其他类时,CRTP是一个不错的选择。