第二章 技术

编译期Assertions

//动态检测
#include <cassert>

template<typename TypeOut, typename TypeIn>
TypeOut safe_reinterpret_cast(TypeIn tIn)
{
    assert(sizeof(TypeIn) <= sizeof(TypeOut));
    return reinterpret_cast<TypeOut>(tIn);
}

int main()
{
    void* pValue = nullptr;
    char nValue0 = reinterpret_cast<char>(pValue);          //正常运行
    char nValue1 = safe_reinterpret_cast<char>(pValue);     //引发中断
    return 0;
}
//静态检测,但是编译器报的错并不直接

//结合常量表达式和编译器自动检查分配0数组错误
#define STATIC_CHECK(expr) { char aTem[expr ? 1 : 0]; }

template<typename TypeOut, typename TypeIn>
TypeOut safe_reinterpret_cast(TypeIn tIn)
{
    STATIC_CHECK(sizeof(TypeIn) <= sizeof(TypeOut));
    return reinterpret_cast<TypeOut>(tIn);
}

int main()
{
    void* pValue = nullptr;
    char nValue0 = reinterpret_cast<char>(pValue);               //编译通过

    //char nValue1 = safe_reinterpret_cast<char>(pValue);        //编译失败
    //error C2466: 不能分配常量大小为 0 的数组

    long long nValue2 = safe_reinterpret_cast<long long>(pValue);//编译成功
    return 0;
}
//最终版:检测任意常量表达式,且能自定义编译器报错的字符串

template<bool> class CCompileTimeCheck
{
public:
    CCompileTimeCheck(...);
};
template<> class CCompileTimeCheck<false> {};


#define STATIC_CHECK(expr, msg)                     \
{                                                   \
    class C##msg {};                                \
    sizeof((CCompileTimeCheck<expr> (C##msg())));   \
}


template<typename TypeOut, typename TypeIn>
TypeOut safe_reinterpret_cast(TypeIn tIn)
{
    STATIC_CHECK(sizeof(TypeOut) >= sizeof(TypeIn), Szn);

    return reinterpret_cast<TypeOut>(tIn);
}


int main()
{
    void* pValue = nullptr;
    long long nValue0 = safe_reinterpret_cast<long long>(pValue);//正常编译

    char nValue1 = safe_reinterpret_cast<char>(pValue);          //编译出错
/*  
    error C2440: “<function-style-cast>”: 
    无法从“safe_reinterpret_cast::CSzn”转换为“CCompileTimeCheck<false>”
*/
    return 0;
}


对象构造、函数声明、名称查找的规则组合体现的一个例子

#include <cstdio>
#include <typeinfo>


class CTest
{
public:
    CTest()             { printf("CTest\n"); }
    CTest(CTest&)       { printf("CTest Copy\n"); }
};

class CTest1
{
public:
    CTest1()            { printf("CTest1\n"); }
    CTest1(CTest&)      { printf("CTest1 Create\n"); }
    CTest1(CTest1&)     { printf("CTest1 Copy\n"); }
};


int main()
{
    {
        int nV0 = double(2.2);          //强制类型转换 nV0 = 2
        //反汇编: 008F3E2C  mov         dword ptr [nV0],2 

        int nV1 = (double)2.2;          //强制类型转换 nV1 = 2
        //反汇编: 008F3E33  mov         dword ptr [nV1],2  

        auto nV2 = int(double());       //强制类型转换 nV2 = 0
        //反汇编: 008F3E3A  mov         dword ptr [nV2],0  

        auto nV3 = static_cast<int>(double());
        //反汇编: 01003E25  mov         dword ptr [nV3],0  

        auto pStr = typeid(int(double())).name();
        //pStr = "int __cdecl(double (__cdecl*)(void))"

        pStr = typeid((int(double()))).name();
        //pStr = "int"

        pStr = typeid(double()).name();
        //pStr = "double __cdecl(void)"

        pStr = typeid((double())).name();
        //pStr = "double"


        //int (double);                 //编译失败
        int Fun(double);                //函数声明
        pStr = typeid(Fun).name();      //pStr = "int __cdecl(double)"

        //sizeof 需要一个表达式或类型
        //sizeof(Fun);  //error Cun2070: “int (double)”: 非法的 sizeof 操作数
        int nV4 = sizeof(Fun(1.1));     //Fun即使不进行定义,也不会报错 nV4 = 4
        //反汇编: 009C3ECD  mov         dword ptr [nV4],4  
    }

    {
        auto Test1 = CTest1(CTest());       //输出"CTest" "CTest1 Create"
        (CTest1(CTest()));                  //输出"CTest" "CTest1 Create"

        CTest1 CTest();                     //函数声明 
        CTest1 (CTest());                   //函数声明 等价上一句
        //从此 名字 CTest就不是自定义的类了,而是一个函数名称

        auto it = CTest1(CTest());  //输出"Suprise" "CTest1" "CTest1 Copy"
        auto it1 = CTest();         //输出"Suprise" "CTest1"

        printf("");
    }

    return 0;
}


CTest1(CTest())     //等价于 CTest1 CTest() 此段代码是是CTest函数的定义
{
    printf("Suprise\n");

    return CTest1();
}


模板偏特化

  • 模板类可以全特化也可以偏特化
  • 模板类的成员函数只能全特化,不能偏特化(参见第一章)
  • namespace-level的函数不能偏特化,但是可以被轻易的重载

常数映射为类型

  • 应用场景:
    有必要根据某个编译期常数调用一个或数个不同的函数
    编译期实施分派(if-else switch语句)
template<int nValue>
struct IntToType
{
    enum { eValue = nValue };
};


template<typename T, bool bTest>
class CTest
{
public:
    void Fun()
    {
        //编译期分派
        if (bTest)
        {
            tValue.SomeFun();
        }
    }

private:
    T tValue;
};

template<typename T, bool bTest>
class CTest1
{
private:
    void Fun(IntToType<true>)
    {
        tValue.SomeFun();
    }

    void Fun(IntToType<false>){}

public:
    //编译期分派
    void Suprise()
    {
        Fun(IntToType<bTest>());
    }

private:
    T tValue;
};


int main()
{
    int nValue = 10;
    auto it0 = IntToType<10>();         //编译成功
    //auto it1 = IntToType<nValue>();   //编译出错
    //“IntToType”: 模板参数“nValue”:“nValue”: 局部变量不能用作非类型参数

    CTest<int, false> Test;
    //Test.Fun();               //编译出错

    CTest1<int, false> Test1;
    Test1.Suprise();            //编译成功
    return 0;
}


类型对类型的映射

//类型映射到类型
template<typename T>
struct STypeToType{ typedef T TypeOriginal; };

template<typename T, typename U>
T* Create(const U& u, STypeToType<T>)
{
    return new T(u);
}

template<typename U>
CTest* Create(const U& u, STypeToType<CTest>)
{
    //根据类型定制的版本
    return new CTest(u, -1);
}


型别选择

  • 一个应用场景:比如根据不同的类型,在vector中选择放置对象本身还是对象的指针
#include <vector>
#include <typeinfo>


using std::vector;


//方法一:扩展性差
template<typename T, bool bRe>
struct STest
{
    typedef T* Type;
};

template<typename T>
struct STest<T, false>
{
    typedef T Type;
};


//方法二:扩展性好
template<bool bRe, typename T, typename U>
struct SSelect
{
    typedef T Type;
};

template<typename T, typename U>
struct SSelect<false, T, U>
{
    typedef U Type;
};


int main()
{
    //it0 = "int *"
    auto it0 = typeid(STest<int, true>::Type).name();

    //it1 =  "int"
    auto it1 = typeid(STest<int, false>::Type).name();

    //it2 = "int"
    auto it2 = typeid(SSelect<true, int, double>::Type).name();

    //it3 = "double"
    auto it3 = typeid(SSelect<false, int, double>::Type).name();    
    
    return 0;
}


编译期间侦查可转换性和继承性

  • 面对两个陌生的类型T和U,如何得知U是否继承于T?在编译期间发掘这样的关系,意味着不必使用dynamic_cast,后者会所耗执行效率。
template<typename T, typename U>
class CConversion
{
    //此类可以用于编译期间侦测类型的可转换性

    typedef char Small;
    class CBig { char aBuff[2]; };
    //定义class CBig { char aBuff[2]; }; 确保sizeof(CBig) != sizeof(Small)

    static Small Fun(U);
    static CBig Fun(...);
/*
    若T能转换为U则调用Fun(U), 否则调用Fun(...)
    则有如下结论:
    若T能转换为U : Fun返回Small
    若T不能转换为U : Fun返回CBig
*/

    static T MakeT();
    //定义一个不完全函数,即使T类型的构造函数是private类型的也无妨

public:
    enum 
    {
        eExists = sizeof(Fun(MakeT())) == sizeof(Small)
        //若T能转换为U则      Exists = true
        //若T不能转换为U则 Exists = false

/*
        sizeof可以用在任何表达式上,不论后者有多复杂,sizeof会直接传回大小,
        不需要等到执行期才评估。这意味着sizeof可以感知重载、模板具现、转换规则
        ,或任何可以发生在C++表达式上的机制
*/
    };

    enum
    {
        eExistsToWay = eExists && CConversion<U, T>::eExists
    };

    enum
    {
        eSameType = false
    };
};


//模板偏特化
template<typename T>
class CConversion<T, T>
{
public:
    enum { eExists = true, eExistsToWay = true, eSameType = true };
};


class CFather {};
class CChild : public CFather {};


int main()
{
    bool aRe0[] = 
    {
        CConversion<double, int>::eExists,      //true
        CConversion<int, char*>::eExists,       //false
        CConversion<CChild*, CFather*>::eExists,//true
        CConversion<CFather*, CChild*>::eExists,//false
    };

    bool aRe1[] = 
    {
        //true
        CConversion<double, int>::eExistsToWay,

        //false
        CConversion<CChild*, CFather*>::eExistsToWay,

    };

    bool aRe2[] =
    {
        CConversion<int, int>::eSameType,       //true
        CConversion<int, double>::eSameType,    //false
    };

    return 0;
}


Type Traits

//类型判断

#include <vector>
using std::vector;


template<typename T>
class CTypeTraits
{
public:
    enum
    {
        eNormal,
        eNference,
        eNormalPoint,
        ePointToMember,
    };

private:
    template<typename U> struct STypeTest
    {
        enum { eResult = eNormal };
    };

    template<typename U> struct STypeTest<U&>
    {
        enum { eResult = eNference };
    };

    template<typename U> struct STypeTest<U* const>
    {
        enum { eResult = eNormalPoint };
    };

    template<typename U> struct STypeTest<U*>
    {
        enum { eResult = eNormalPoint };
    };

    template<typename U, typename V>
    struct STypeTest<U V::*>
    {
        enum { eResult = ePointToMember };
    };

    template<typename U, typename V>
    struct STypeTest<U (V::*)() const>
    {
        //类的常量 成员/成员函数 指针
        enum { eResult = ePointToMember };
    };

public:
    enum { eType = STypeTest<T>::eResult };
};


struct A
{
    int value;
    int Fun();
};


int main()
{
    //语法 : 定义一个指向类的常量成员函数的指针
    vector<int>::size_type (vector<int>::*p)() const = &vector<int>::size;

    char aRe[] = 
    {
        CTypeTraits<vector<char>::iterator>::eType, //0
        CTypeTraits<char>::eType,                   //0
        CTypeTraits<char&>::eType,                  //1
        CTypeTraits<char* const>::eType,            //2
        CTypeTraits<char*>::eType,                  //2
        CTypeTraits<decltype(&A::value)>::eType,    //3
        CTypeTraits<decltype(&A::Fun)>::eType,      //3
        CTypeTraits<decltype(&vector<int>::size)>::eType,       //3
    };

    return 0;
}
//利用偏特化 去掉 const 属性
template<typename T>
class CDelConst
{
private:
    template<typename U> struct SNotConst
    {
        typedef U Type;
    };

    template<typename U> struct SNotConst<const U>
    {
        typedef U Type;
    };

public:
    typedef typename SNotConst<T>::Type Type;
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容