C/C++编程:policy trait

1059 篇文章 285 订阅
  • property trait:用于确定模板参数的一些属性。比如这些参数表示什么类型,在混合类型的操作中,应该提升哪一个类型等等。
  • policy trait:定义如何对待这些类型。

只读的参数类型

在C/C++中,函数调用实参在缺省情况下都是以传值方式进行传递的。也就是说,由调用者计算出来的实参值需要被拷贝到被调用者所控制的位置中;但是如果参数是很大的数据结构,这种拷贝非常耗资源;因此,对于这种数据,应该传递const引用;如果参数是很小的结构,从性能的观点看,究竟采用何种机制依赖于代码所在的实际体系结果。实际上,小的数据结果究竟采用何种机制,对性能的影响不大;但是,我们也必须小心处理,选择一个适当的传递机制

当引入模板后,事情就更加复杂了:因为我们事先并不知道用来替换模板参数的类型究竟有多大,而且,最后的决定也不仅仅依赖于类型的大小:一个小的结构也可能会具有昂贵的拷贝构造函数,这是我们也应该以const 引用的方式来传递只读参数

可以使用policy trait模板来处理上面的问题,而且该policy trait模板实际上是一个类型函数:该函数可以根据不同的情况(即类型大小),将把实参类型T映射为T或者T const&,即在这两种类型中挑选出一种最佳参数类型。我们来看个例子:

template<bool C, typename Ta, typename Tb>
class IfThenElse;

//  局部特化: true 的话选择第二个实参
template<typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
    typedef Ta ResultT;
};

// 局部特化: false 的话选择第三个实参
template<typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
    typedef Tb ResultT;
};

template<typename T>
class RParam{
public:
    typedef typename IfThenElse<sizeof(T) <= 2*sizeof(void *),
            T,
            T const &>::ResultT Type;
};

上面模板实现了:对于不大于”2个指针“大小的类型,基本模板将采用传值的方式传递参数,而对于其他的类型,将采用传递const引用的方式传递参数。

另外,对于容器里下,即使sizeof函数返回的是一个很小的值,但也可能涉及到昂贵的拷贝构造函数。因此,编写如下的特化:

template<typename T>
class RParam<Array<T>>{
public:
    typedef Array<T> const & Type;
};

由于我们处理的都是C++的常见类型,所以我们期望在基本模板中能够对非class类型以传值的方式进行调用。另外对于某些对性能要求比较苛刻的class类型,我们有选择的添加这些类为传值方式:

template<bool C, typename Ta, typename Tb>
class IfThenElse;

//  局部特化: true 的话选择第二个实参
template<typename Ta, typename Tb>
class IfThenElse<true, Ta, Tb>{
public:
    typedef Ta ResultT;
};

// 局部特化: false 的话选择第三个实参
template<typename Ta, typename Tb>
class IfThenElse<false, Ta, Tb>{
public:
    typedef Tb ResultT;
};



template<typename T>
class IsClassT {
private:
    typedef char One;
    typedef struct { char a[2]; } Two;
    template<typename C> static One test(int C::*);
    template<typename C> static Two test(...);
public:
    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
    enum { No = !Yes };
};

template<typename T>
class RParam {
public:
    typedef typename IfThenElse<IsClassT<T>::No,
            T,
            T const&>::ResultT Type;
};

使用:假设我们有两个类,其中一个指定:对于只读参数而言,传值调用具有更好的性能:

class MyClass1 {
  public:
    MyClass1 () {
    }
    MyClass1 (MyClass1 const&) {
        std::cout << "MyClass1 copy constructor called\n";
    }
};

class MyClass2 {
  public:
    MyClass2 () {
    }
    MyClass2 (MyClass2 const&) {
        std::cout << "MyClass2 copy constructor called\n";
    }
};

//对于RParam<>的MyClass2 参数,传值 
template<>
class RParam<MyClass2> {
  public:
    typedef MyClass2 Type;
};

使用:

template<typename T1, typename T2>
void foo(typename RParam<T1>::Type p1,
        typename RParam<T2>::Type p2){

}
int main()
{
    MyClass1 myClass1;
    MyClass2 myClass2;
    foo<MyClass1, MyClass2>(myClass1, myClass2);
}

上面的实用有几个严重的缺点:函数声明变得复杂,而且现在不能使用实参演绎来获取比如foo()的函数了,因为模板参数只是出现在函数参数的限定符里面。

解决上面的问题的方法:使用一个内联的包装(wrapper)函数模板:但是该方案假设内联函数将会被编译器移除,即编译器将直接调用位于内联函数里面的函数:

template <typename T1, typename T2>
void foo_core (typename RParam<T1>::Type p1,
          typename RParam<T2>::Type p2)
{
    //...
}

// 为了避免显式指定模板参数而实现的wrapper
template <typename T1, typename T2>
inline
void foo (T1 const & p1, T2 const & p2)
{
    foo_core<T1,T2>(p1,p2);
}


int main()
{
    MyClass1 myClass1;
    MyClass2 myClass2;
    foo(myClass1, myClass2);// ==> foo<MyClass1, MyClass2>(myClass1, myClass2);
}

拷贝、交换和移动

本节将引入一个policy trait模板:它将选择出最佳的操作,来拷贝、交换或者移动某一特定类型的元素

假设拷贝操作是通过拷贝构造函数和拷贝赋值运算符来实现的。对于单一元素而言,这是完全正确的。但是对于具有相同类型的多个元素而言,与重复地调用该类型的构造函数或者赋值运算符相比,可能还存在效率更高的操作。

容器类型就是一个典型的例子。实际上,对容器类型而言,拷贝操作通常是不允许的,而主要是进行交换或者移动操作。

因此,我们期望能够用一个合适的trait模板,来确定上面的问题(最佳操作)。对于泛型定义,我们将区分class和nonclass类型,因为定于nonclass类型而言,我们并不需要在意用户自定义的拷贝构造函数和拷贝赋值运算符。这一次,我们将

  • 定义一个基本模板(CMS表示”copy、move、swap“)。
// primary template
template<typename T, bool Bitwise>
class BitOrClassCSM;
  • 使用继承,从而能够在两种trait实现中进行选择。
template <typename T>
class CSMtraits : public BitOrClassCSM<T, IsClassT<T>::No > {
};

CSMtraits 的实现完全委托给类BitOrClassCSM的特化

  • 实现BitOrClassCSM
// 用于对象安全拷贝的局部特化
template<typename T>
class BitOrClassCSM<T, false> {
  public:
    static void copy (typename RParam<T>::Type src, T* dst) {
        // 把其中一项拷贝给对应对的另一项
        *dst = src;
    }

    static void copy_n (T const* src, T* dst, size_t n) {
        //把其中n项拷贝给其他n项
        for (size_t k = 0; k<n; ++k) {
            dst[k] = src[k];
        }
    }

    static void copy_init (typename RParam<T>::Type src,
                           void* dst) {
        //拷贝一项到未进行初始化的存储空间
        ::new(dst) T(src);
    }

    static void copy_init_n (T const* src, void* dst, size_t n) {
        //拷贝n项到未进行初始化的存储空间
        for (size_t k = 0; k<n; ++k) {
            ::new((void*)((T*)dst+k)) T(src[k]);
        }
    }

    static void swap (T* a, T* b) {
        //交换1项
        T tmp(*a);
        *a = *b;
        *b = tmp;
    }

    static void swap_n (T* a, T* b, size_t n) {
        //交换n项
        for (size_t k = 0; k<n; ++k) {
            T tmp(a[k]);
            a[k] = b[k];
            b[k] = tmp;
        }
    }

    static void move (T* src, T* dst) {
        // 移动1项到另一项
        assert(src != dst);
        *dst = *src;
        src->~T();
    }

    static void move_n (T* src, T* dst, size_t n) {
        // 移动n项到另一项
        assert(src != dst);
        for (size_t k = 0; k<n; ++k) {
            dst[k] = src[k];
            src[k].~T();
        }
    }

    static void move_init (T* src, void* dst) {
        // 移动一项到未初始化的存储空间
        assert(src != dst);
        ::new(dst) T(*src);
        src->~T();
    }

    static void move_init_n (T const* src, void* dst, size_t n) {
        // 移动n项到未初始化的存储空间
        assert(src != dst);
        for (size_t k = 0; k<n; ++k) {
            ::new((void*)((T*)dst+k)) T(src[k]);
            src[k].~T();
        }
    }
};

上面policy trait模板的成员函数都是静态的。实际上,大多数情况也是如此,因为我们只是对参数类型的对象应用这些成员函数,而并非对trait class类类型的对象应用这些成员函数

最后,另一个针对位元拷贝的trait的局部特化如下:

// 针对更快的对象位元拷贝而实现的局部特化
template <typename T>
class BitOrClassCSM<T,true> : public BitOrClassCSM<T,false> {
  public:
    static void copy_n (T const* src, T* dst, size_t n) {
        // copy n items onto n other ones
        std::memcpy((void*)dst, (void*)src, n*sizeof(T));
    }

    static void copy_init_n (T const* src, void* dst, size_t n) {
        // copy n items onto uninitialized storage
        std::memcpy(dst, (void*)src, n*sizeof(T));
    }

    static void move_n (T* src, T* dst, size_t n) {
        // move n items onto n other ones
        assert(src != dst);
        std::memcpy((void*)dst, (void*)src, n*sizeof(T));
    }

    static void move_init_n (T const* src, void* dst, size_t n) {
        // move n items onto uninitialized storage
        assert(src != dst);
        std::memcpy(dst, (void*)src, n*sizeof(T));
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值