07 其他traits技术
7.1 If-Then-Else
- 之前的PlusResultT traits最终的定义有一个完全不同的实现,它依赖于另一个type traits,HasPlusT的结果。可以用特殊的类型模板IfThenElse构建这个行为,它带有一个布尔非类型模板参数来选择两种类型参数之一
// traits/ifthenelse.hpp
#ifndef IFTHENELSE_HPP
#define IFTHENELSE_HPP
// primary template: yield the second argument by default and rely on
// a partial specialization to yield the third argument if COND is false
template<bool COND, typename TrueType, typename FalseType>
struct IfThenElseT {
using Type = TrueType;
};
// partial specialization: false yields third argument
template<typename TrueType, typename FalseType>
struct IfThenElseT<false, TrueType, FalseType> {
using Type = FalseType;
};
template<bool COND, typename TrueType, typename FalseType>
using IfThenElse =
typename IfThenElseT<COND, TrueType, FalseType>::Type;
#endif // IFTHENELSE_HPP
- 下面的例子演示了一个此模板的应用,它定义了一个类型函数来对一个给定值确定最低级别的整型
// traits/smallestint.hpp
#include <limits>
#include "ifthenelse.hpp"
template<auto N>
struct SmallestIntT {
using Type =
typename IfThenElseT<N <= std::numeric_limits<char> ::max(), char,
typename IfThenElseT<N <= std::numeric_limits<short> ::max(), short,
typename IfThenElseT<N <= std::numeric_limits<int> ::max(), int,
typename IfThenElseT<N <= std::numeric_limits<long>::max(), long,
typename IfThenElseT<N <= std::numeric_limits<long long>::max(),
long long, // then
void // fallback
>::Type
>::Type
>::Type
>::Type
>::Type;
};
- 注意,不同于常规的if-then-else语句,这里then和else分支的模板实参在被选择前都会被估算,所以不能有非法的代码。比如一个traits对给定的带符号类型产生对应的无符号类型,有一个标准traits,std::make_unsigned,但它要求传递类型是带符号整型且不为bool,否则产生ub,下面这个天真的实现就无法工作
// ERROR: undefined behavior if T is bool or no integral type:
template<typename T>
struct UnsignedT {
using Type = IfThenElse<std::is_integral<T>::value
&& !std::is_same<T,bool>::value,
typename std::make_unsigned<T>::type,
T>;
};
- 实例化UnsignedT<bool>仍是ub,因为编译器仍会尝试从下式构建类型
typename std::make_unsigned<T>::type
- 为了指出这个问题,需要添加一个间接级别,使得IfThenElse实参是本身对包裹结果的类型函数的使用
// yield T when using member Type:
template<typename T>
struct IdentityT {
using Type = T;
};
// to make unsigned after IfThenElse was evaluated:
template<typename T>
struct MakeUnsignedT {
using Type = typename std::make_unsigned<T>::type;
};
template<typename T>
struct UnsignedT {
using Type = typename IfThenElse<std::is_integral<T>::value
&& !std::is_same<T,bool>::value,
MakeUnsignedT<T>,
IdentityT<T>
>::Type;
};
- 在UnsignedT定义中,IfThenElse的类型实参都是类型函数本身的实例化。然而类型函数不是在IfThenElse选择一个之前实例化,而是选择类型函数实例(MakeUnsignedT或IdentityT的实例)前,::Type再估计选择的类型函数实例来产生Type。必须强调的是,这完全依赖于IfThenElse中未选择的包裹类型从不完全实例化的事实,特别地,下面代码不能工作
template<typename T>
struct UnsignedT {
using Type = typename IfThenElse<std::is_integral<T>::value
&& !std::is_same<T,bool>::value,
MakeUnsignedT<T>::Type,
T
>::Type;
};
- 必须之后使用MakeUnsignedT<T>的::Type,这意味着需要IdentityT辅助也要在else分支中之后使用T的::Type,因此不能使用如下
template<typename T>
using Identity = typename IdentityT<T>::Type;
- 可以声明这样的别名模板,它可能在其他地方有用,但不能将它有效利用于IfThenElse的定义,因为Identity<T>的使用会立刻造成Identity<T>的实例化来取回它的Type成员
- IfThenElse模板在标准库中是可用的,为std::conditional<>,用它可以如下定义UnsignedT
template<typename T>
struct UnsignedT {
using Type
= typename std::conditional_t<std::is_integral<T>::value
&& !std::is_same<T,bool>::value,
MakeUnsignedT<T>,
IdentityT<T>
>::Type;
};
7.2 检查不抛出异常的操作
- 确定一个特殊的操作能否抛出异常有时是有用的。比如,一个移动构造函数应该被标记为noexcept,然而这通常依赖于它的成员的基类是否抛异常。比如下面是一个简单类模板Pair的移动构造函数
template<typename T1, typename T2>
class Pair {
T1 first;
T2 second;
public:
Pair(Pair&& other)
: first(std::forward<T1>(other.first)),
second(std::forward<T2>(other.second)) {
}
};
- 剩下要做的就是实现IsNothrowMoveConstructibleT traits,可以用noexcept操作符来保证表达式不抛异常直接实现
// traits/isnothrowmoveconstructible1.hpp
#include <utility> // for declval
#include <type_traits> // for bool_constant
template<typename T>
struct IsNothrowMoveConstructibleT
: std::bool_constant<noexcept(T(std::declval<T>()))>
{
};
- 因为noexcept结果是一个布尔值,可以直接传递定义基类std::bool_constant<>,它用于定义std::true_type和std::false_type。然而这个实现需要改进,因为它不是SFINAE-friendly:如果traits对一个没有有用的移动或构造函数的类型初始化,使得T(std::declval<T&&>())无效,则整个程序非法
class E {
public:
E(E&&) = delete;
};
...
std::cout << IsNothrowMoveConstructibleT<E>::value; // 编译期错误
- 这里traits应该产生一个false值而不是报错。这里必须在检查移动构造函数是否为noexcept前确定它是否有效,因此修改第一个版本的traits,添加一个默认为void的模板参数和一个使用std::void_t参数的局部特化,仅当构造函数有效时后者的实参有效
// traits/isnothrowmoveconstructible2.hpp
#include <utility> // for declval
#include <type_traits> // for true_type, false_type, and bool_constant<>
// primary template:
template<typename T, typename = std::void_t<>>
struct IsNothrowMoveConstructibleT : std::false_type
{
};
// partial specialization (may be SFINAE'd away):
template<typename T>
struct IsNothrowMoveConstructibleT
<T, std::void_t<decltype(T(std::declval<T>()))>>
: std::bool_constant<noexcept(T(std::declval<T>()))>
{
};
- 如果局部特化中std::void_t<...>的替换有效,特化被选择,否则被丢弃不被实例化,而是实例化基本模板,产生一个std::false_type结果
- 注意,如果不能直接访问移动构造函数则无法检查它是否抛异常,移动构造函数是public且不是deleted是不够的,还要对应的类型不是抽象类(但抽象类的引用或指针可以),因此traits名为IsNothrowMoveConstructible而不是HasNothrowMove-Constructor
- 标准库提供了对应的type traits std::is_move_constructible<>
7.3 traits的便利性
- 一个关于traits常见的抱怨就是冗长,每次使用type traits都要一个尾置的::Type,一个typename关键词,多个type traits组合时将使得格式十分笨拙,比如对数组operator+,正确实现保证不返回常量或引用
template<typename T1, typename T2>
Array<
typename RemoveCVT<
typename RemoveReferenceT<
typename PlusResultT<T1, T2>::Type
>::Type
>::Type
>
operator+ (Array<T1> const&, Array<T2> const&);
- 通过使用别名模板和变量模板,使用这个traits,可以更方便地产生对应类型或值,但注意一些context中不能使用这些简写,必须使用原始的类模板
7.3.1 别名模板和traits
- 别名模板可以简化代码,比如下面三个别名模板包裹了上述的type traits
template<typename T>
using RemoveCV = typename RemoveCVT<T>::Type;
template<typename T>
using RemoveReference = typename RemoveReferenceT<T>::Type;
template<typename T1, typename T2>
using PlusResult = typename PlusResultT<T1, T2>::Type;
Click here to view code image
template<typename T1, typename T2>
Array<RemoveCV<RemoveReference<PlusResultT<T1, T2>>>>
operator+ (Array<T1> const&, Array<T2> const&);
- 然而使用别名模板也有一些缺点
- 别名模板不能被特化,traits的许多技术依赖于特化,此时只能把别名模板改回类模板
- 一些traits有意让用户特化,比如描述一个特殊的附加操作是否可替换,当大多数使用涉及别名模板时,特化类模板就会造成迷惑
- 使用别名模板总会实例化类型,将使得对给定类型难以避免无意义的实例化traits。换句话说,别名模板不能用于元函数转发
7.3.2 变量模板和traits
- traits返回一个值需要一个尾置的::value来产生结果,这种情况下constexpr变量模板可以简化代码。下面的变量模板包裹了之前的IsSameT和IsConvertibleT
template<typename T1, typename T2>
constexpr bool IsSame = IsSameT<T1,T2>::value;
template<typename FROM, typename TO>
constexpr bool IsConvertible = IsConvertibleT<FROM, TO>::value;
if (IsSameT<T,int>::value || IsConvertibleT<T,char>::value) ...
// 简写为
if (IsSame<T,int> || IsConvertible<T,char>) ...
08 类型分类(Type Classification)
- 有时需要知道模板参数是内置类型、指针类型还是类类型等,下面将开发一系列用于确定一个给定类型的各种属性的traits。结果将可以对一些类型写出
if (IsClassT<T>::value) {
...
}
if constexpr (IsClass<T>) {
...
}
class C { // primary template for the general case
...
};
template<typename T>
class C<T, true> { // partial specialization for class types
...
};
8.1 判断基本类型
- 先开发一个确定基本类型的模板,默认假设一个类型不是基本类型,并对基本类型的情况特化
// traits/isfunda.hpp
#include <cstddef> // for nullptr_t
#include <type_traits> // for true_type, false_type, and bool_constant<>
// primary template: in general T is not a fundamental type
template<typename T>
struct IsFundaT : std::false_type {
};
// macro to specialize for fundamental types
#define MK_FUNDA_TYPE(T) \
template<> struct IsFundaT<T> : std::true_type { \
};
MK_FUNDA_TYPE(void)
MK_FUNDA_TYPE(bool)
MK_FUNDA_TYPE(char)
MK_FUNDA_TYPE(signed char)
MK_FUNDA_TYPE(unsigned char)
MK_FUNDA_TYPE(wchar_t)
MK_FUNDA_TYPE(char16_t)
MK_FUNDA_TYPE(char32_t)
MK_FUNDA_TYPE(signed short)
MK_FUNDA_TYPE(unsigned short)
MK_FUNDA_TYPE(signed int)
MK_FUNDA_TYPE(unsigned int)
MK_FUNDA_TYPE(signed long)
MK_FUNDA_TYPE(unsigned long)
MK_FUNDA_TYPE(signed long long)
MK_FUNDA_TYPE(unsigned long long)
MK_FUNDA_TYPE(float)
MK_FUNDA_TYPE(double)
MK_FUNDA_TYPE(long double)
MK_FUNDA_TYPE(std::nullptr_t)
#undef MK_FUNDA_TYPE
- 基本模板定义通用情况,IsFundaT<T>::value为false,对每个基本类型定义一个特化,IsFundaT<T>::value为true,为了方便定义了一个宏扩展代码
MK_FUNDA_TYPE(bool)
// 扩展为
template<> struct IsFundaT<bool> : std::true_type {
static constexpr bool value = true;
};
// traits/isfundatest.cpp
#include "isfunda.hpp"
#include <iostream>
template<typename T>
void test (T const&)
{
if (IsFundaT<T>::value) {
std::cout << "T is a fundamental type" << '\n';
}
else {
std::cout << "T is not a fundamental type" << '\n';
}
}
int main()
{
test(7); // T is a fundamental type
test("hello"); // T is not a fundamental type
}
8.2 判断复合类型
- 简单复合类型包括指针类型、左值和右值引用类型、pointer-to-member类型、数组类型。类类型和函数类型也是复合类型,但他们的组合能涉及任意数量的类型。枚举类型在这里被认为是非简单复合类型。简单复合类型可以用局部特化分类
- 指针。标准库提供了std::is_pointer<>
// traits/ispointer.hpp
template<typename T>
struct IsPointerT : std::false_type { // primary template: by default not a pointer
};
template<typename T>
struct IsPointerT<T*> : std::true_type { // partial specialization for pointers
using BaseT = T; // type pointing to
};
- 引用。标准库提供了std::is_lvalue_reference<>,std::is_rvalue_reference<>以及std::is_reference<>
// traits/islvaluereference.hpp
template<typename T>
struct IsLValueReferenceT : std::false_type { // by default no lvalue reference
};
template<typename T>
struct IsLValueReferenceT<T&> : std::true_type { // unless T is lvalue references
using BaseT = T; // type referring to
};
// traits/isrvaluereference.hpp
template<typename T>
struct IsRValueReferenceT : std::false_type { // by default no rvalue reference
};
template<typename T>
struct IsRValueReferenceT<T&&> : std::true_type { // unless T is rvalue reference
using BaseT = T; // type referring to
};
// traits/isreference.hpp
#include "islvaluereference.hpp"
#include "isrvaluereference.hpp"
#include "ifthenelse.hpp"
template<typename T>
class IsReferenceT
: public IfThenElseT<IsLValueReferenceT<T>::value,
IsLValueReferenceT<T>,
IsRValueReferenceT<T>
>::Type {
};
- 数组。标准库提供了std::is_array<>,同时提供了std::rank<>和std::extent<>来允许查询大小
// traits/isarray.hpp
#include <cstddef>
template<typename T>
struct IsArrayT : std::false_type { // primary template: not an array
};
template<typename T, std::size_t N>
struct IsArrayT<T[N]> : std::true_type { // partial specialization for arrays
using BaseT = T;
static constexpr std::size_t size = N;
};
template<typename T>
struct IsArrayT<T[]> : std::true_type { // partial specialization for unbound arrays
using BaseT = T;
static constexpr std::size_t size = 0;
};
- pointer-to-member。标准库提供了std::is_member_object_pointer<>,std::is_member_function_pointer<>以及std::is_member_pointer<>
// traits/ispointertomember.hpp
template<typename T>
struct IsPointerToMemberT : std::false_type { // by default no pointer-to-member
};
template<typename T, typename C>
struct IsPointerToMemberT<T C::*> : std::true_type { // partial specialization
using MemberT = T;
using ClassT = C;
};
8.3 判断函数类型
- 函数类型有任意数量的参数影响结果,因此在匹配函数类型的局部特化中,借用一个参数包来捕获所有的参数类型,就像之前对DecayT traits做的
// traits/isfunction.hpp
#include "../typelist/typelist.hpp"
template<typename T>
struct IsFunctionT : std::false_type { // primary template: no function
};
template<typename R, typename... Params>
struct IsFunctionT<R (Params...)> : std::true_type
{ //functions
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = false;
};
template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...)> : std::true_type { // variadic functions
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = true;
};
- 不幸的是,IsFunctionT的构建不能处理所有函数类型,因为函数类型可以带cv限定符、左值右值引用限定符,以及C++17的noexcept限定符,比如
using MyFuncType = void (int&) const;
- 这样的函数类型只能用于nonstatic成员函数。此外标记为const的函数类型不是真的const类型,RemoveConst无法去除const。因此为了识别带限定符的函数类型,需要引入大量的附加的局部特化来覆盖所有的限定符组合。这里只阐述其中五个
template<typename R, typename... Params>
struct IsFunctionT<R (Params...) const> : std::true_type {
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = false;
};
template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) volatile> : std::true_type {
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = true;
};
template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) const volatile> :
std::true_type {
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = true;
};
template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) &> : std::true_type {
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = true;
};
template<typename R, typename... Params>
struct IsFunctionT<R (Params..., ...) const&> : std::true_type {
using Type = R;
using ParamsT = Typelist<Params...>;
static constexpr bool variadic = true;
};
...
8.4 判断类类型
- 不同于已经处理过的其他组合类型,类类型没有局部特化模式,也不能像对基本类型那样枚举所有类类型。这里需要用一个间接的方法识别类类型——提出一些对所有类类型都有效的类型或表达式,对其可以应用SFINAE
- 类类型最方便利用的属性是,只有类类型能被用作pointer-to-member类型的基础,即在Y::*这个构造中,Y只能为类类型
// traits/isclass.hpp
#include <type_traits>
template<typename T, typename = std::void_t<>>
struct IsClassT : std::false_type { // primary template: by default no class
};
template<typename T>
struct IsClassT<T, std::void_t<int T::*>> // classes can have pointer-to-member
: std::true_type {
};
- lambda表达式是一个unique unnamed non-union class type,因此检查lambda表达式是否为类类型对象时将产生true。注意,表达式int T::*对union类型也有效,union也是一种类类型
auto l = []{};
static_assert<IsClassT<decltype(l)>::value, "">; // succeeds
- 标准库提供了std::is_class<>和std::is_union<>
- 下面是C++98中确定类类型的方法
#include <iostream>
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>
void check()
{
if (IsClassT<T>::Yes) std::cout << "Y" << std::endl;
else std::cout << "N" << std::endl;
}
template<typename T>
void checkT (T)
{
check<T>();
}
class MyClass {};
struct MyStruct {};
union MyUnion {};
void myfunc() {}
enum E{e1}e;
int main()
{
check<int>(); // N
check<MyClass>(); // Y
MyStruct s;
checkT(s); // Y
check<MyUnion>(); // Y
checkT(e); // N
checkT(myfunc); // N
}
8.5 判断枚举类型
- 测试枚举类型,直接用SFINAE-based traits检查一个到int的显式转换,并显式排除基本类型、类类型、引用类型、指针类型和pointer-to-member类型,所有这些能转为int的都不是枚举类型。简单来说就是,不属于其他任何类别的类型就是枚举类型
// traits/isenum.hpp
template<typename T>
struct IsEnumT {
static constexpr bool value = !IsFundaT<T>::value &&
!IsPointerT<T>::value &&
!IsReferenceT<T>::value &&
!IsArrayT<T>::value &&
!IsPointerToMemberT<T>::value &&
!IsFunctionT<T>::value &&
!IsClassT<T>::value;
};
09 Policy traits
- 目前给出的traits模板例子都是用于去确定模板参数的一些属性,如这些参数表示的类型,混合类型的操作中需要提升的类型,这些traits称为property traits。另外还存在其他类型的traits,定义了如何对待这些类型,这类traits称为policy traits,policy traits针对的是模板参数相关的更加独有的属性。通常把property traits实现为类型函数,但policy traits通常把policy封装在成员函数内部
9.1 只读的参数类型
- 不能预测替换模板参数的类型大小,而一个小的结构也可能有高开销的拷贝构造函数,这时应该以const引用方式传递只读参数。使用policy traits模板可以处理这个问题,它实际上是一个类型函数,这个函数可以根据类型大小把实参类型T映射为T或T const&。下面的例子假设对不大于2个指针大小的类型,基本模板pass-by-value,对其他类型则传const引用
template<typename T>
class RParam {
public:
using Type = typename IfThenElseT<sizeof(T)<=2*sizeof(void*),
T,
T const&>::ResultT Type;
};
- 另一方面,对容器类型,即使sizeof很小也可能涉及昂贵的拷贝构造函数,因此需要如下的许多特化和局部特化
template<typename T>
struct RParam<Array<T>> {
using Type = Array<T> const&;
};
- 因为处理的是常见类型,所以希望在基本模板中对nonclass type传值调用,对某些性能要求严格的class type则有选择地添加这些类为传值方式
// traits/rparam.hpp
#ifndef RPARAM_HPP
#define RPARAM_HPP
#include "ifthenelse.hpp"
#include <type_traits>
template<typename T>
struct RParam {
using Type
= IfThenElse<(sizeof(T) <= 2*sizeof(void*)
&& std::is_trivially_copy_constructible<T>::value
&& std::is_trivially_move_constructible<T>::value),
T,
T const&>;
};
#endif // RPARAM_HPP
- 对于上面两种方法的任意一种,都可以在traits模板的定义中实现这个policy,客户端能使用这个policy获得更好的性能。比如假设有两个类,其中一个指定对只读实参,传值性能更好
// traits/rparamcls.hpp
#include "rparam.hpp"
#include <iostream>
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";
}
};
// pass MyClass2 objects with RParam<> by value
template<>
class RParam<MyClass2> {
public:
using Type = MyClass2;
};
- 现在对具有只读实参的函数就可以在函数声明中使用RParam<>,并调用这些函数
// traits/rparam1.cpp
#include "rparam.hpp"
#include "rparamcls.hpp"
// function that allows parameter passing by value or by reference
template <typename T1, typename T2>
void foo (typename RParam<T1>::Type p1,
typename RParam<T2>::Type p2)
{
...
}
int main()
{
MyClass1 mc1;
MyClass2 mc2;
foo<MyClass1,MyClass2>(mc1,mc2);
}
- 但使用RParam的做法有几个严重缺点,函数声明变得格外复杂, 其次不能使用实参推断来调用类似foo()这样的函数,因为模板参数只出现在函数参数的限定符中,因此必须在调用的位置显式指定模板实参。有一个笨拙的解决方法是,使用一个内联的wrapper函数模板,但该方案假设内联函数会被编译器移除,即编译器直接调用位于内联函数中的函数
// traits/rparam2.cpp
#include "rparam.hpp"
#include "rparamcls.hpp"
// function that allows parameter passing by value or by reference
template <typename T1, typename T2>
void foo_core (typename RParam<T1>::Type p1, typename RParam<T2>::Type p2)
{
...
}
// wrapper to avoid explicit template parameter passing
template<typename T1, typename T2>
void foo (T1 && p1, T2 && p2)
{
foo_core<T1,T2>(std::forward<T1>(p1),std::forward<T2>(p2));
}
int main()
{
MyClass1 mc1;
MyClass2 mc2;
foo(mc1,mc2); // same as foo_core<MyClass1,MyClass2>(mc1,mc2)
}
9.2 拷贝、交换和移动(第一版内容)
- 这里介绍一个policy traits模板,它将选择出最佳的操作,来拷贝、交换或移动某一特定类型的元素
- 期望一个traits模板,对于泛型定义区分class type和nonclass type,因为对nonclass type不需要关心用户定义的copying操作,这里使用继承来在两种traits中进行选择
// traits/csmtraits.hpp
template <typename T>
class CSMtraits : public BitOrClassCSM<T, IsClassT<T>::No > {
};
- CSMtraits(copy、swap、move的traits)的实现委托给了BitOrClassCSM<>的特化,基类的第二个模板参数表示是否能安全地使用bitwise copying实现多种操作。泛型定义假设不能对class类型安全地使用bitwise copying,但对POD类型可以特化CSMtraits获得更好的性能
template<>
class CSMtraits<MyPODType>
: public BitOrClassCSM<MyPODType, true> {
};
- BitOrClassCSM模板在缺省情况下包含两个局部特化,下面是一个基本模板和一个不使用bitwise copying的安全的局部特化
// traits/csm1.hpp
#include <new>
#include <cassert>
#include <stddef.h>
#include "rparam.hpp"
// primary template
template<typename T, bool Bitwise>
class BitOrClassCSM;
// partial specialization for safe copying of objects
template<typename T>
class BitOrClassCSM<T, false> {
public:
static void copy (typename RParam<T>::ResultT src, T* dst) {
// copy one item onto another one
*dst = src;
}
static void copy_n (T const* src, T* dst, size_t n) {
// copy n items onto n other ones
for (size_tk=0;k<n; ++k) {
dst[k] = src[k];
}
}
static void copy_init (typename RParam<T>::ResultT src, void* dst) {
// copy an item onto uninitialized storage
::new(dst) T(src);
}
static void copy_init_n (T const* src, void* dst, size_t n) {
// copy n items onto uninitialized storage
for (size_tk=0;k<n; ++k) {
::new((void*)((char*)dst+k)) T(src[k]);
}
}
static void swap (T* a, T* b) {
// swap two items
T tmp(a);
*a = *b;
*b = tmp;
}
static void swap_n (T* a, T* b, size_t n) {
// swap n items
for (size_tk=0;k<n; ++k) {
T tmp(a[k]);
a[k] = b[k];
b[k] = tmp;
}
}
static void move (T* src, T* dst) {
// move one item onto another
assert(src != dst);
*dst = *src;
src->~T();
}
static void move_n (T* src, T* dst, size_t n) {
// move n items onto n other ones
assert(src != dst);
for (size_tk=0;k<n; ++k) {
dst[k] = src[k];
src[k].~T();
}
}
static void move_init (T* src, void* dst) {
// move an item onto uninitialized storage
assert(src != dst);
::new(dst) T(*src);
src->~T();
}
static void move_init_n (T const* src, void* dst, size_t n) {
// move n items onto uninitialized storage
assert(src != dst);
for (size_tk=0;k<n; ++k) {
::new((void*)((char*)dst+k)) T(src[k]);
src[k].~T();
}
}
};
- 上面policy traits模板成员函数都是静态的,因为只是对参数类型的对象应用这些成员函数,而并非对traits class类型对象使用
- 另一个针对bitwise copying的traits实现的局部特化如下
// traits/csm2.hpp
#include <cstring>
#include <cassert>
#include <stddef.h>
#include "csm1.hpp"
// partial specialization for fast bitwise copying of objects
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);
}
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);
}
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);
}
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);
}
};
10 In the Standard Library
- C++11中,type traits变成了标准库的内置部分。他们或多或少包含上述所有类型函数和type traits,但对于其中一些,比如trivial operation detection traits和std::is_union,没有已知的in-language solution,而编译器对这些traits提供了内置支持。此外即使有in-language solution来缩短编译期,编译器也开始支持traits。因此如果需要type traits,只要可行就推荐使用标准库
- 标准库也定义了一些policy和property traits
- 类模板std::char_traits被string和I/O stream类用作一个policy traits参数
- 为了轻易适配算法给标准迭代器,提供了一个非常简单的property traits模板std::iterator_traits
- 模板std::numeric_limits也能被用作property traits模板
- 最后,内存分配器通过使用policy traits类处理,C++98开始就提供了模板std::allocator作为此目的的标准组件,C++11添加了模板std::allocator_traits来允许改变分配器的policy/behavior
网友评论