模板与继承
命名模板参数
问题描述
解决具有默认模板的类实例化过程中,前面的模板参数还需要明确指定默认模板的问题。
解决方案
设法将缺省类型值放入到一个基类中,在根据需要通过派生类覆盖掉某些类型值,具体的代码如下:
template <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> {
};
// default policies
class DefaultPolicy1 {};
class DefaultPolicy2 {};
class DefaultPolicy3 {
public:
static void doPrint() {
std::cout << "DefaultPolicy3::doPrint()\n";
}
};
class DefaultPolicy4 {};
// define default policies as P1, P2, P3, P4
class DefaultPolicies {
public:
typedef DefaultPolicy1 P1;
typedef DefaultPolicy2 P2;
typedef DefaultPolicy3 P3;
typedef DefaultPolicy4 P4;
};
// class to define a usage of the default policy values
// - avoids ambiguities if we derive from DefaultPolicies more than once
class DefaultPolicyArgs : virtual public DefaultPolicies {
};
// class templates to override the default policy values
template <typename Policy>
class Policy1_is : virtual public DefaultPolicies {
public:
typedef Policy P1; // overriding typedef
};
template <typename Policy>
class Policy2_is : virtual public DefaultPolicies {
public:
typedef Policy P2; // overriding typedef
};
template <typename Policy>
class Policy3_is : virtual public DefaultPolicies {
public:
typedef Policy P3; // overriding typedef
};
template <typename Policy>
class Policy4_is : virtual public DefaultPolicies {
public:
typedef Policy P4; // overriding typedef
};
// create class template with four policies and default values
template <typename PolicySetter1 = DefaultPolicyArgs,
typename PolicySetter2 = DefaultPolicyArgs,
typename PolicySetter3 = DefaultPolicyArgs,
typename PolicySetter4 = DefaultPolicyArgs>
class BreadSlicer {
typedef PolicySelector<PolicySetter1, PolicySetter2,
PolicySetter3, PolicySetter4>
Policies;
// use Policies::P1, Policies::P2, //... to refer to the various policies.
public:
void print () {
Policies::P3::doPrint();
}
//...
};
// define a custom policy
class CustomPolicy {
public:
static void doPrint() {
std::cout << "CustomPolicy::doPrint()\n";
}
};
int main()
{
BreadSlicer<> bc1;
bc1.print();
BreadSlicer<Policy3_is<CustomPolicy> > bc2;
bc2.print();
}
空基类优化
定义
C++因为布局的需要,空类的大小一般不为0,通常为1,有的甚至为4。
C++规定,当空类作为基类时,只要不会与同一类型的对象或子对象分配到同一地址,就不需要为其分配任何空间,通常称为空类优化(EBCO)技术。
布局原则
实例分析1
class Empty {
typedef int Int; // typedef members don't make a class nonempty
};
class EmptyToo : public Empty {
};
class EmptyThree : public EmptyToo {
};
int main()
{
std::cout << "sizeof(Empty): " << sizeof(Empty)
<< '\n';
std::cout << "sizeof(EmptyToo): " << sizeof(EmptyToo)
<< '\n';
std::cout << "sizeof(EmptyThree): " << sizeof(EmptyThree)
<< '\n';
}
在支持EBCO技术的编译器下,Empty、EmptyToo、EmptyThree的大小都为1。而在不支持EBCO技术的编译器下,Empty、EmptyToo、EmptyThree可能分别为1、2、3.
实例分析2
class Empty {
typedef int Int; // typedef members don't make a class nonempty
};
class EmptyToo : public Empty {
};
class NonEmpty : public Empty, public EmptyToo {
};
int main()
{
std::cout << "sizeof(Empty): " << sizeof(Empty) << '\n';
std::cout << "sizeof(EmptyToo): " << sizeof(EmptyToo) << '\n';
std::cout << "sizeof(NonEmpty): " << sizeof(NonEmpty) << '\n';
}
由于NonEmpty的基类EmptyToo和Empty不能分配到同一地址空间,否则EmptyToo的基类Empty和NonEmpty的基类Empty撞在同一地址空间,因此,NonEmpty的可能大小为2.
成员作基类
问题描述
在如下例子中:
template <typename T1, typename T2>
class MyClass
{
T1 a;
T2 b;
...
}
模板参数T1或T2之一或全部都有可能为空类,因此上面的类可能不能具有最优的布局,每个这样的实例都有可能浪费一个字的内存。
解决办法
借助EBCO的东风,采用继承的方式,示例如下:
template <typename Base, typename Member>
class BaseMemberPair : private Base {
private:
Member member;
public:
// constructor
BaseMemberPair (Base const & b, Member const & m)
: Base(b), member(m) {
}
// access base class data via first()
Base const& first() const {
return (Base const&)*this;
}
Base& first() {
return (Base&)*this;
}
// access member data via second()
Member const& second() const {
return this->member;
}
Member& second() {
return this->member;
}
};
奇特的递归模板模式(CRTP)
定义和语法
派生类作为模板传给基类。
template <typename Derived>
class CuriousBase
{
...
}
class Curios: public CuriousBase<CuriousBase>
{
...
}
实例
1、记录类的实例化的个数
2、boost中的enable_shared_from_this类采用了此方法。
参数化虚拟性
定义和语法
定义
C++允许通过模板直接参数化3种实体:类型、常数和模板,同时,模板还能间接参数化其他特性,比如成员函数的虚拟性。
语法
class NotVirtual {
};
class Virtual {
public:
virtual void foo() {
}
};
template <typename VBase>
class Base : private VBase {
public:
// the virtuality of foo() depends on its declaration
// (if any) in the base class VBase
// foo的虚拟性依赖于它在基类(如果存在基类的话)VBase的声明
void foo() {
std::cout << "Base::foo()" << '\n';
}
};
template <typename V>
class Derived : public Base<V> {
public:
void foo() {
std::cout << "Derived::foo()" << '\n';
}
};
int main()S
{
Base<NotVirtual>* p1 = new Derived<NotVirtual>;
p1->foo(); // calls Base::foo()
Base<Virtual>* p2 = new Derived<Virtual>;
p2->foo(); // calls Derived::foo()
}