1.1 命名模板参数优化
许多模板类有非常多的参数例如:
template <class T1 = BreadPolicy1,class T2 = BreadPolicy2,class T3 = BreadPolicy3,class T4 = BreadPolicy4>
class BreadSlicer{
...
}
但在大多情况下,模板类都有默认的参数,但是如果我想修改其中的一个参数,例如我要修改T3的默认参数那么我就得知道T1,T2,因为输入缺省参数得知道前面所有参数得值。
有以下的编程技巧可以解决这个问题。
核心思路思路是通过一个辅助类PolicySelector
将所有的变量类型封装到其中,下面是步骤
#include <iostream>class Policy1{};class Policy2{
public:static void do_print(){std::cout << "Policy2 do print" <<std::endl;}
};class Policy3{static void do_print(){std::cout << "Policy3 do print" <<std::endl;}
};class Policy4{};
// 假设上面的类使我们最终要传递的模板参数。class DefaultPolicy{
public:typedef Policy1 p1;typedef Policy2 p2;typedef Policy3 p3;typedef Policy4 p4;
};
// 首先通过DefaultPolicy 将这4类型都重新命名到一个类中,最终直接通过p1,p2,p3,p4完成操作class BreadPolicy :virtual public DefaultPolicy{
};
// BreadPolicy 用于作为默认参数template <class T>
class Policy1_is : virtual public DefaultPolicy{
public:typedef T p1;
};template <class T>
class Policy2_is : virtual public DefaultPolicy{
public:typedef T p2;
};template <class T>
class Policy3_is : virtual public DefaultPolicy{
public:typedef T p3;
};template <class T>
class Policy4_is : virtual public DefaultPolicy{
public:typedef T p4;
};//Policy1_is, Policy2_is, Policy3_is, Policy4_is 这四个类中都有p1,p2,p3,p4这四个类型,然后将外部新定义的类型覆盖之前定义的。 这里需要使用虚拟继承,原因后面说template <class Base, int N>
class Discriminator : public Base{};template <class T1,class T2,class T3,class T4>
class PolicySelector : public Discriminator<T1, 1>,public Discriminator<T2, 2>,public Discriminator<T3, 3>,public Discriminator<T4, 4>
{
};template <class T1 = BreadPolicy,class T2 = BreadPolicy,class T3 = BreadPolicy,class T4 = BreadPolicy>
class BreadSlicer{
public:void print(){policies::p2::do_print();policies::p3::do_print();}
private:typedef PolicySelector<T1, T2, T3, T4> policies;
};class PolicyP2{
public:static void do_print(){std::cout << "test policy p2" << std::endl;}
};class PolicyP3{
public:static void do_print(){std::cout << "test policy p3" << std::endl;}
};int main(){BreadSlicer test;test.print();std::cout << std::endl;BreadSlicer<Policy2_is<PolicyP2>> test1;test1.print();std::cout << std::endl;BreadSlicer<Policy2_is<PolicyP3>, Policy3_is<PolicyP2>> test2;test2.print();std::cout << std::endl;
}
在最终的调用这BreadSlicer
中,将传入的所有模板参数重新用一个类保管,所有的参数类型都被封装到了 PolicySelector
该类初始化的时候会依次继承传入的类,所有在上文中BreadPolicy和PolicyP… 都是用的是virtual,这样会避免重复继承。
当使用默认参数得时候,看上去是继承类四次,但实际上
首先BreadSlicer<Policy2_is<PolicyP2>> test1;
这样的传参实际上就是就是传入类第一个参数,其他三个参数用的都是默认值,之后PolicySelector分别继承Discriminator 1,2,3,4, 由于Policy2_is
和 BreadPolicy
都是派生类,它们里面都拥有P1, P2,P3,P4,这样最后在继承Discriminator的时候将新传入的参数覆盖掉了之前的参数。
Policy2 do print
Policy3 do printtest policy p2
Policy3 do printtest policy p3
test policy p2进程已结束,退出代码0
上述代码运行结果
上面的方法成功解决了填写一个默认参数得问题,但是如果想随意填写两个默认参数,那么就会出现问题,只有
1.2 空基类优化
在C++中如果一个类没有任何成员变量,那么编译器会将其大小设置为1。
但是如果有下面的情况:
template<class T1, class T2>
class Opt{
public:T1 info1; //模板参数作为成员变量,可能为null, 那么它大小为1会占用一个字节T2 info2; //又会占用一个字节
};
如果T1和T2都是空类,那么Opt
就会是2个字节,也许我们可以
class opt :private T1, private T2 {...
}
也许可以这样,但是T2,和T2还有别的可能,如果是int, 或者不是class,那么上面的写法不成立
假设我们知道一个参数必然是class,而另外一个参数不是类,那可以有以下的方法
template<class Base, class type>
class BaseMemberPair : private Base{
private:type member_;
public:BaseMemberPair(Base const& base, type member): Base(base), member_(member){}const Base& first() const{return (Base* const &) *this;}Base& first() {return (Base* &) *this;}const type& Second() const {return member_;}type& Second() {return member_;}
};template<class T1, class T2>
class Option{
public:Option(T1& t, T2 val):info_and_storage(t, val){}private:BaseMemberPair<T1, T2> info_and_storage;
};
将这两个参数封装到BaseMemberPair
中,因为有成员变量的存在,BaseMemberPair
不会为空类,从而消除了可能存在的额外空间使用。
int main(){std::cout << sizeof(Opt<Empty2, Empty2>) << std::endl;Empty ss;Option<Empty, int> cc(ss, 1);Opt<Empty, int> aa;std::cout << sizeof(cc) << std::endl;std::cout << sizeof(aa) << std::endl;
}
/*
2
4
8
*/
最终结果中,使用Option更加省空间, 一个对象省类4bt的空间。在Opt的大小为8, 应该是存在内存对其的问题。不过不管怎么样确实省下了字节