精华内容
下载资源
问答
  • C++强制类型转换

    千次阅读 2021-04-20 18:41:34
    C++ 类型转换(C风格的强制转换): 在C++基本的数据类型中,可以分为四类:整型,浮点型,字符型,布尔型。其中数值型包括 整型与浮点型;字符型即为char。 (1)将浮点型数据赋值给整型变量时,舍弃其小数部分。...

    C++ 类型转换(C风格的强制转换):

    在C++基本的数据类型中,可以分为四类:整型,浮点型,字符型,布尔型。其中数值型包括 整型与浮点型;字符型即为char。

    (1)将浮点型数据赋值给整型变量时,舍弃其小数部分。

    (2)将整型数据赋值给浮点型变量时,数值不变,但是以指数形式存储。

    (3)将double型数据赋值给float型变量时,注意数值范围溢出。

    (4)字符型数据可以赋值给整型变量,此时存入的是字符的ASCII码。

    (5)将一个int,short或long型数据赋值给一个char型变量,只将低8位原封不动的送到char型变量中。 
    (6)将有符号型数据赋值给长度相同的无符号型变量,连同原来的符号位一起传送。

     

    C++强制类型转换:

    在C++语言中新增了四个关键字static_cast、const_cast、reinterpret_cast和dynamic_cast。这四个关键字都是用于强制类型转换的。

    新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。

    C++中风格是static_cast<type>(content)。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。 

     

    1) static_cast

    在C++语言中static_cast用于数据类型的强制转换,强制将一种数据类型转换为另一种数据类型。例如将整型数据转换为浮点型数据。
    [例1]C语言所采用的类型转换方式:

    int a = 10;
    int b = 3;
    double result = (double)a / (double)b;

    例1中将整型变量a和b转换为双精度浮点型,然后相除。在C++语言中,我们可以采用static_cast关键字来进行强制类型转换,如下所示。
    [例2]static_cast关键字的使用:

    int a = 10;
    int b = 3;
    double result = static_cast<double>(a) / static_cast<double>(b);

    在本例中同样是将整型变量a转换为双精度浮点型。采用static_cast进行强制数据类型转换时,将想要转换成的数据类型放到尖括号中,将待转换的变量或表达式放在元括号中,其格式可以概括为如下形式:    

    用法:static_cast <类型说明符> (变量或表达式)

    它主要有如下几种用法:
        (1)用于类层次结构中基类和派生类之间指针或引用的转换
          进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
          进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的
        (2)用于基本数据类型之间的转换,如把int转换成char。这种转换的安全也要开发人员来保证
        (3)把空指针转换成目标类型的空指针
        (4)把任何类型的表达式转换为void类型
        注意:static_cast不能转换掉expression的const、volitale或者__unaligned属性。

    static_cast:可以实现C++中内置基本数据类型之间的相互转换。

    如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数。

     

    2) const_cast

    在C语言中,const限定符通常被用来限定变量,用于表示该变量的值不能被修改。

    而const_cast则正是用于强制去掉这种不能被修改的常数特性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。

    用法:const_cast<type_id> (expression)
        该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
        常量指针被转化成非常量指针,并且仍然指向原来的对象;
        常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。

    [例3]一个错误的例子:

    const int a = 10;
    const int * p = &a;
    *p = 20;                  //compile error
    int b = const_cast<int>(a);  //compile error

    在本例中出现了两个编译错误,第一个编译错误是*p因为具有常量性,其值是不能被修改的;另一处错误是const_cast强制转换对象必须为指针或引用,而例3中为一个变量,这是不允许的!

    [例4]const_cast关键字的使用

    #include<iostream>
    using namespace std;
    
    
    int main()
    {
        const int a = 10;
    
        const int * p = &a;
    
        int *q;
    
        q = const_cast<int *>(p);
    
        *q = 20;    //fine
    
        cout <<a<<" "<<*p<<" "<<*q<<endl;
    
            cout <<&a<<" "<<p<<" "<<q<<endl;
    
        return 0;
    }

    在本例中,我们将变量a声明为常量变量,同时声明了一个const指针指向该变量(此时如果声明一个普通指针指向该常量变量的话是不允许的,Visual Studio 2010编译器会报错)。

     

    之后我们定义了一个普通的指针*q。将p指针通过const_cast去掉其常量性,并赋给q指针。之后我再修改q指针所指地址的值时,这是不会有问题的。

    最后将结果打印出来,运行结果如下:
    10 20 20
    002CFAF4 002CFAF4 002CFAF4

    查看运行结果,问题来了,指针p和指针q都是指向a变量的,指向地址相同,而且经过调试发现002CFAF4地址内的值确实由10被修改成了20,这是怎么一回事呢?为什么a的值打印出来还是10呢?

    其实这是一件好事,我们要庆幸a变量最终的值没有变成20!变量a一开始就被声明为一个常量变量,不管后面的程序怎么处理,它就是一个常量,就是不会变化的。试想一下如果这个变量a最终变成了20会有什么后果呢?对于这些简短的程序而言,如果最后a变成了20,我们会一眼看出是q指针修改了,但是一旦一个项目工程非常庞大的时候,在程序某个地方出现了一个q这样的指针,它可以修改常量a,这是一件很可怕的事情的,可以说是一个程序的漏洞,毕竟将变量a声明为常量就是不希望修改它,如果后面能修改,这就太恐怖了。

    在例4中我们称“*q=20”语句为未定义行为语句,所谓的未定义行为是指在标准的C++规范中并没有明确规定这种语句的具体行为,该语句的具体行为由编译器来自行决定如何处理。对于这种未定义行为的语句我们应该尽量予以避免!

    从例4中我们可以看出我们是不想修改变量a的值的,既然如此,定义一个const_cast关键字强制去掉指针的常量性到底有什么用呢?我们接着来看下面的例子。

    例5:

    #include<iostream>
    using namespace std;
    
    const int * Search(const int * a, int n, int val);
    
    int main()
    
    {
    
        int a[10] = {0,1,2,3,4,5,6,7,8,9};
    
        int val = 5;
    
        int *p;
    
        p = const_cast<int *>(Search(a, 10, val));
    
        if(p == NULL)
    
            cout<<"Not found the val in array a"<<endl;
    
        else
    
            cout<<"hvae found the val in array a and the val = "<<*p<<endl;
    
        return 0;
    
    }
    
    const int * Search(const int * a, int n, int val)
    
    {
    
        int i;
    
        for(i=0; i<n; i++)
    
        {
    
            if(a[i] == val)
    
                return &a[i];
    
        }
    
        return  NULL;
    
    }

    在例5中我们定义了一个函数,用于在a数组中寻找val值,如果找到了就返回该值的地址,如果没有找到则返回NULL。函数Search返回值是const指针,当我们在a数组中找到了val值的时候,我们会返回val的地址,最关键的是a数组在main函数中并不是const,因此即使我们去掉返回值的常量性有可能会造成a数组被修改,但是这也依然是安全的。

    对于引用,我们同样能使用const_cast来强制去掉常量性,如例6所示。

    例6:

    #include<iostream>
    using namespace std;
    const int & Search(const int * a, int n, int val);
    
    int main()
    
    {
    
    int a[10] = {0,1,2,3,4,5,6,7,8,9};
    
    int val = 5;
    
    int &p = const_cast<int &>(Search(a, 10, val));
    
    if(p == NULL)
    
    cout<<"Not found the val in array a"<<endl;
    
    else
    
    cout<<"hvae found the val in array a and the val = "<<p<<endl;
    
    return 0;
    
    }
    
    
    const int & Search(const int * a, int n, int val)
    
    {
    
    int i;
    
    for(i=0; i<n; i++)
    
    {
    
    if(a[i] == val)
    
    return a[i];
    
    }
    
    return NULL;
    
    }

     了解了const_cast的使用场景后,可以知道使用const_cast通常是一种无奈之举,同时也建议大家在今后的C++程序设计过程中一定不要利用const_cast去掉指针或引用的常量性并且去修改原始变量的数值,这是一种非常不好的行为。

     

    3) reinterpret_cast

    在C++语言中,reinterpret_cast主要有三种强制转换用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型

    用法:reinterpret_cast<type_id> (expression)
        type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
        它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
        在使用reinterpret_cast强制转换过程仅仅只是比特位的拷贝,因此在使用过程中需要特别谨慎!

    例7:

    int *a = new int;
    double *d = reinterpret_cast<double *>(a);

    在例7中,将整型指针通过reinterpret_cast强制转换成了双精度浮点型指针。

    reinterpret_cast可以将指针或引用转换为一个足够长度的整形,此中的足够长度具体长度需要多少则取决于操作系统,如果是32位的操作系统,就需要4个字节及以上的整型,如果是64位的操作系统则需要8个字节及以上的整型。 

     

    4) dynamic_cast

     用法:dynamic_cast<type_id> (expression)

     

    (1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。

    (2)不能用于内置的基本数据类型的强制转换。

    (3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。

    (4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。

            B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。

            这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,

            只有定义了虚函数的类才有虚函数表。

     (5)在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

            向上转换,即为子类指针指向父类指针(一般不会出问题);向下转换,即将父类指针转化子类指针。

           向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

            在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。

     

    例1:

    #include<iostream>
    
    using namespace std;
    
     
    
    class base
    
    {
    
    public :
    
        void m(){cout<<"m"<<endl;}
    
    };
    
    
    class derived : public base
    {
    public:
    
        void f(){cout<<"f"<<endl;}
    
    };
    
    int main()
    
    {
    
        derived * p;
    
        p = new base;
    
        p = static_cast<derived *>(new base);
    
        p->m();
    
        p->f();
    
        return 0;
    
    }

    本例中定义了两个类:base类和derived类,这两个类构成继承关系。在base类中定义了m函数,derived类中定义了f函数。在前面介绍多态时,我们一直是用基类指针指向派生类或基类对象,而本例则不同了。

    本例主函数中定义的是一个派生类指针,当我们将其指向一个基类对象时,这是错误的,会导致编译错误。

    但是通过强制类型转换我们可以将派生类指针指向一个基类对象,p = static_cast<derived *>(new base);语句实现的就是这样一个功能,这样的一种强制类型转换时合乎C++语法规定的,但是是非常不明智的,它会带来一定的危险。

    在程序中p是一个派生类对象,我们将其强制指向一个基类对象,首先通过p指针调用m函数,因为基类中包含有m函数,这一句没有问题,之后通过p指针调用f函数。一般来讲,因为p指针是一个派生类类型的指针,而派生类中拥有f函数,因此p->f();这一语句不会有问题,但是本例中p指针指向的确实基类的对象,而基类中并没有声明f函数,虽然p->f();这一语句虽然仍没有语法错误,但是它却产生了一个运行时的错误。换言之,p指针是派生类指针,这表明程序设计人员可以通过p指针调用派生类的成员函数f,但是在实际的程序设计过程中却误将p指针指向了一个基类对象,这就导致了一个运行期错误。

    产生这种运行期的错误原因在于static_cast强制类型转换时并不具有保证类型安全的功能,而C++提供的dynamic_cast却能解决这一问题,dynamic_cast可以在程序运行时检测类型转换是否类型安全。

    当然dynamic_cast使用起来也是有条件的,它要求所转换的操作数必须包含多态类类型(即至少包含一个虚函数的类)。

    例2:

    #include<iostream>
    using namespace std;
    
    class base
    
    {
    
    public :
    
        void m(){cout<<"m"<<endl;}
    
    };
    
    class derived : public base
    
    {
    public:
    
        void f(){cout<<"f"<<endl;}
    
    };
    
    
    int main()
    {
    
        derived * p;
    
        p = new base;
    
        p = dynamic_cast<derived *>(new base);
    
        p->m();
    
        p->f();
    
        return 0;
    }

    在本例中利用dynamic_cast进行强制类型转换,但是因为base类中并不存在虚函数,因此p = dynamic_cast<derived *>(new base);这一句会编译错误。

    为了解决本例中的语法错误,我们可以将base类中的函数m声明为虚函数,virtual void m(){cout<<"m"<<endl;}。

    dynamic_cast还要求<>内部所描述的目标类型必须为指针或引用。

    例3:

    #include<iostream>
    #include<cstring>
    using namespace std;
     
     
    class A
    {
    public:
       virtual void f()
       {
           cout<<"hello"<<endl;
     
           };
     
    };
     
     
    class B:public A
    {
    public:
     
        void f()
        {
            cout<<"hello2"<<endl;
        } 
    };
     
     
    class C
    {
      void pp()
      {
          return;
      }
     
    };
     
    int fun()
    {
        return 1;
    }
     
    int main()
    {
        A* a1=new B;//a1是A类型的指针指向一个B类型的对象
     
        A* a2=new A;//a2是A类型的指针指向一个A类型的对象
     
        B* b;
     
        C* c;
     
        b=dynamic_cast<B*>(a1);//结果为not null,向下转换成功,a1之前指向的就是B类型的对象,所以可以转换成B类型的指针。
     
        if(b==NULL)
        {
            cout<<"null"<<endl;
        }
        else
        {
     
            cout<<"not null"<<endl;
     
        }
     
        b=dynamic_cast<B*>(a2);//结果为null,向下转换失败
     
        if(b==NULL)
        {
            cout<<"null"<<endl;
     
        }
        else
        {
            cout<<"not null"<<endl;
        }
     
     
        c=dynamic_cast<C*>(a);//结果为null,向下转换失败
     
        if(c==NULL)
     
        {
            cout<<"null"<<endl;
        }
        else
        {
            cout<<"not null"<<endl;
     
        }
     
        delete(a);
        return 0;
    }

     

    展开全文
  • C++ 强制类型转换

    千次阅读 2020-01-07 21:00:07
    强制类型转换 C++语言中提供了static_cast、const_cast、reinterpret_cast和dynamic_cast。这四个关键字都是用于强制类型转换的。 二.static_cast 1.基本数据之间的转换,如int转化为double int a = 1; ...

    一.强制类型转换

    C++语言中提供了static_cast、const_cast、reinterpret_cast和dynamic_cast。这四个关键字都是用于强制类型转换的。 

    二.static_cast

    1.基本数据之间的转换,如int转化为double

    int a = 1;
    double b = static_cast<double>(a);

    2.用于类层次结构中基类和派生类之间指针或引用的转换
    ①进行上行转换(把派生类的指针或引用转换成基类表示)是安全的
    ②进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的

    //写两个类,假如 Derive类继承Basic
    Basic *b = nullptr;
    Derive *d = new Derive;
    b = static_cast<Basic *> d;

    3.把空指针转换成目标类型的空指针

    int *p;
    void *m = malloc(sizeof(int));
    p = static_cast<int *>m;

    4.把任何类型的表达式转换为void类型 

    三.const_cast

    cost_cast即用于强制转换指针或者引用的const或volatile限制,特别注意的是,const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。

    //错误例子
    const int a = 2;
    int b = const_cast<int>a;        //错误,const_cast强制转换的对象必须是指针或引用
    
    //正确例子
    int c = 2;
    const int *d = &c;
    int *e = const_cast<int *>d;

    四.reinterpret_cast 

    reinterpret_cast运算符用于处理无关类型之间的转换,他会产生一个新的值,这个值会有与原始参数(原数据类型)有完全相同的比特位。

    1.从指针类型到一个足够大的整数类型

    2.从整数类型或者枚举类型到指针类型

    3.从一个指向函数的指针到另一个不同类型的指向函数的指针

    4.从一个指向对象的指针到另一个不同类型的指向对象的指针

    5.从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针

    6.从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针

    五.dynamic_cast

    1.前面三种都是编译时完成的强制转换,dynamic_cast是运行时处理的。

    2.不能用于内置的基本数据类型的强制转换。

    3.dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。

    4.使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。

    5.在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

    使用dynamic_cast进行向下强制类型转换。使用此关键字有以下几个条件:

    ①.必须有虚函数

    ②.必须打开编译器的RTTI开关

    ③.必须有继承关系 

    展开全文
  • c++ 强制类型转换

    千次阅读 2018-09-12 23:33:43
    C++语言中,如果类从包含虚函数的基类派生,则指向基类类型的指针可用于调用派生类对象中包含的虚函数的实现。 包含虚函数的类有时被称为“多态类”。 由于派生类完全包含它派生自的所有基类的定义,因此在类层次...

    版权归原网站,仅供学习参考。

    在C++语言中,如果类从包含虚函数的基类派生,则指向基类类型的指针可用于调用派生类对象中包含的虚函数的实现。 包含虚函数的类有时被称为“多态类”。

    由于派生类完全包含它派生自的所有基类的定义,因此在类层次结构上将指针转换至这些基类中的任何一个是安全的。 提供一个指向基类的指针,在层次结构中向下转换指针可能是安全的。 如果将指向的对象实际上是从基类派生的类型,则是安全的。 在这种情况下,实际对象称为“完整对象”。指向基类的指针称为指向完整对象的“子对象”。 例如,考虑下图中显示的类层次结构。

    类层次结构
    类层次结构

    如下图所示,可对类型为 C 的对象进行可视化。

    具有子对象 B 和 A 的类 C
    带有 B 子对象和 A 子对象的类 C

    给定 C 类的一个实例,存在 B 子对象和 A 子对象。 包括 AB 子对象的 C 实例是“完整对象。”

    通过使用运行时类型信息,可以检查指针实际是否指向完整对象,并可以安全转换以指向其层次结构中的另一个对象。 dynamic_cast 运算符可用于进行这些类型的转换。 它还执行必要的运行时检查以确保操作安全。

    对于非多态类型的转换,可以使用 static_cast 运算符(本主题说明静态和动态强制转换之间的差异,以及何时适合使用它们)。

    1、 c强制转换与c++强制转换

     c语言强制类型转换主要用于基础的数据类型间的转换,语法为:

    (type-id)expression//转换格式1
    
    type-id(expression)//转换格式2

    c++除了能使用c语言的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要运用于继承关系类间的强制转化,语法为:

    static_cast<new_type>      (expression)
    dynamic_cast<new_type>     (expression) 
    const_cast<new_type>       (expression) 
    reinterpret_cast<new_type> (expression)

    备注:new_type为目标数据类型,expression为原始数据类型变量或者表达式。

    《Effective C++》中将c语言强制类型转换称为旧式转型,c++强制类型转换称为新式转型

    2、static_cast、dynamic_cast、const_cast、reinterpret_cast

    a): static_cast 转换:用隐式和用户定义转换的组合在类型间转换。

    语法

    static_cast < new_type > ( expression )

    返回 new_type 类型的值。

    唯有下列转换能用 static_cast 执行,除了这种转换会转型走常性易变性的情况。

    1) 若有从 expression 到 new_type 的隐式转换序列,或若new_type 类型对象或引用的自 expression 的直接初始化会找到至少一个可生成函数,则 static_cast<new_type>(expression) 返回如同以 new_type Temp(expression); 初始化的虚构变量 Temp ,它可能涉及隐式转换、对 new_type 构造函数的调用或对用户定义转换运算符的调用。对于非引用的 new_type , static_cast 纯右值表达式的结果对象是被直接初始化者 (C++17 起)

    2) 若 new_type 是到某类类型 D 的指针或引用,且 expression 的类型是到其非虚基类 B 的指针或引用,则 static_cast 进行向下转型。若 BD 的歧义、不可访问或虚基类(或虚基类的基类),则此向下转型病式。这种 static_cast 不进行运行时检查以确保该对象的运行时类型确实是 D ,而且仅若此前提条件为其他方法所保证才能安全使用,例如在实现静多态时。可以用 dynamic_cast 执行安全的向下转型。

    3) 若 new_type 是右值引用类型,则 static_cast 转换泛左值、类纯右值或数组纯右值 (C++17 前)任何左值 (C++17 起) expression 的值为与该表达式指代相同对象,或指代其基类子对象(取决于 new_type )的亡值。若目标类型是表达式的不可访问或有歧义基类,则程序为病式。若表达式是位域左值,则它首先被转换成底层类型的纯右值。此类型的 static_cast 用于在 std::move 中实现移动语义。

    (C++11 起)

    4) 若 new_type 是 void 类型(可为 cv 限定),则 static_cast 在求值 expression 后舍弃其值。

    5) 若存在从 new_type 到 expression 类型的标准转换序列,且它不包含左值到右值、数组到指针、函数到指针、空指针、空成员指针、函数指针 (C++17 起)或布尔转换,则 static_cast 能进行该隐式转换的逆转换。

    6) 若从 expression 到 new_type 设计左值到右值、数组到指针或函数到指针转换,则能显式用 static_cast 进行它。

    7) 有作用域枚举类型能转换成整数或浮点类型。当目标类型是 cv bool 时,若原值为零则结果为 false ,而对所有其他值结果为 true 。对于其余整数类型,若它们表示为目标类型,则结果是枚举的值,否则结果未指定。(C++11 起)

    8) 整数或枚举类型值能转换为任何完整枚举类型

    • 若底层类型不固定,则若 expression 的值落在范围(范围是大到足以保有目标枚举的所有枚举项的最小位域的所有可能值)外则结果未指定 (C++17 前)为未定义行为 (C++17 起)。
    • 若底层类型固定,则结果同转换原值到枚举的底层类型再到该枚举类型的结果。

    9) 指向某类 D 成员的指针能向上转型为指向其无歧义、可访问的基类 B 。此 static_cast 不进行检查以确保成员实际存在于所指向对象的运行时类型。

    10) 指向(可有 cv 限定) void 指针类型的纯右值能转换到指向任何对象指针类型。若原指针值所表示的内存中字节地址不满足目标类型对齐要求,则结果指针值未指定。否则,若原指针值指向对象 a ,而有与 a 指针可互转换(定义于下)的目标类型(忽略 cv 限定)对象 b ,则结果为指向 b 的指针。否则指针值不改变。任何指针转换到指向 void 指针,再转换回指向原(或更为 cv 限定的)类型的指针,都保持其原值。

    同所有转型表达式,结果是:

    • 左值,若 new_type 是左值引用或到函数类型的右值引用;
    • 亡值,若 new_type 是到对象类型的右值引用;
    • 否则为纯右值。

    二个对象 ab 指针可互转换,若:

    • 它们为同一对象,或
    • 一个是 union 对象而另一个是该对象的非静态数据成员,或
    • 一个是标准布局类对象,而另一个是该对象的首个非静态数据成员,或若该对象无非静态数据成员,则为该对象的首个基类子对象,或
    • 存在对象 c 使得 ac 指针可互转换,而 cb 指针可互转换。
    union U { int a; double b; } u;
    void* x = &u;                        // x 的值为“指向 u 的指针”
    double* y = static_cast<double*>(x); // y 的值为“指向 u.b 的指针”
    char* z = static_cast<char*>(x);     // z 的值为“指向 u 的指针”

    注意:static_cast 亦可用于消歧义函数重载,通过进行到指定类型的函数到指针转换,如于 std::transform(s.begin(), s.end(), s.begin(), static_cast<int(*)(int)>(std::toupper));

    运行此代码

    #include <vector>
    #include <iostream>
     
    struct B {
        int m = 0;
        void hello() const {
            std::cout << "Hello world, this is B!\n";
        }
    };
    struct D : B {
        void hello() const {
            std::cout << "Hello world, this is D!\n";
        }
    };
     
    enum class E { ONE = 1, TWO, THREE };
    enum EU { ONE = 1, TWO, THREE };
     
    int main()
    {
        // 1: 初始化转换
        int n = static_cast<int>(3.14); 
        std::cout << "n = " << n << '\n';
        std::vector<int> v = static_cast<std::vector<int>>(10);
        std::cout << "v.size() = " << v.size() << '\n';
     
        // 2: 静态向下转型
        D d;
        B& br = d; // 通过隐式转换向上转型
        br.hello();
        D& another_d = static_cast<D&>(br); // 向下转型
        another_d.hello();
     
        // 3: 左值到右值
        std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
        std::cout << "after move, v.size() = " << v.size() << '\n';
     
        // 4: 弃值表达式
        static_cast<void>(v2.size());
     
        // 5. 隐式转换的逆
        void* nv = &n;
        int* ni = static_cast<int*>(nv);
        std::cout << "*ni = " << *ni << '\n';
     
        // 6. 数组到指针后随向上转型
        D a[10];
        B* dp = static_cast<B*>(a);
     
        // 7. 有作用域枚举到 int 或 float
        E e = E::ONE;
        int one = static_cast<int>(e);
        std::cout << one << '\n';
     
        // 8. int 到枚举,枚举到另一枚举
        E e2 = static_cast<E>(one);
        EU eu = static_cast<EU>(e2);
     
        // 9. 指向成员指针向上转型
        int D::*pm = &D::m;
        std::cout << br.*static_cast<int B::*>(pm) << '\n';
     
        // 10. void* 到任何类型
        void* voidp = &e;
        std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
    }

    输出:

    n = 3
    v.size() = 10
    Hello world, this is B!
    Hello world, this is D!
    after move, v.size() = 0
    *ni = 3
    1
    0

    b): dynamic_cast 转换:沿继承层级向上、向下及侧向转换到类的指针和引用。

    语法

    dynamic_cast < new_type > ( expression )
    new_type-指向完整类类型的指针、到完整类类型的引用,或指向(可选的 cv 限定) void 的指针
    expression-若 new_type 为引用,则为完整类类型的左值 (C++11 前)泛左值 (C++11 起)表达式,若 new_type 为指针,则为指向完整类类型的指针纯右值。

    若转型成功,则 dynamic_cast 返回 new_type 类型的值。若转型失败且 new_type 是指针类型,则它返回该类型的空指针。若转型失败且 new_type 是引用类型,则它抛出匹配类型 std::bad_cast 处理块的异常。

    解释

    仅下列转换能用 dynamic_cast 进行,除了这种转换会转换走常性易变性的情况。

    1) 若 expression 的类型恰是 new_type 或 new_type 的较少 cv 限定版本,则结果是 expression 带 new_type 类型的值。(换言之, dynamic_cast 可用以添加常性。隐式转型和 static_cast 亦能进行此转换。)

    2) 若 expression 是空指针值,则结果是 new_type 类型的空指针值。

    3) 若 new_type 是到 Base 的指针或引用,且 expression 的类型是到 Derived 的指针或引用,其中 BaseDerived 的单一可访问基类,则结果是到 expression 所标识的对象中 Base 类子对象的指针或引用。(注意:隐式转型和 static_cast 亦能进行此转换。)

    4) 若 expression 是指向多态类型的指针,且 new_type 是到 void 的指针,则结果是指向 expression 所指向或引用对象的最终派生类的指针。

    5) 若 expression 是到多态类型 Base 的指针或引用,且 new_type 是到 Derived 类型的指针或引用,则进行运行时检查:

    a) 检验 expression 所指向/标识的最终派生类对象。若在该对象中 expression 指向/指代 Derived 的公开基类,且只有一个 Derived 类型子对象从 expression 所指向/标识的子对象派生,则转型结果指向/指代该 Derived 子对象。(此之谓“向下转型”。)

    b) 否则,若 expression 指向最终派生类的公开基类,而同时最终派生类拥有 Derived 类型的无歧义公开基类,则转型结果指向/指代该 Derived (此之谓“侧向转型”。)

    c) 否则,运行时检查失败。若 dynamic_cast 用于指针,则返回 new_type 类型空指针值。若它用于引用,则抛出 std::bad_cast 异常。

    6) dynamic_cast 用于构造函数或析构函数(直接或间接),且 expression 指代正在构造/析构的对象时,该对象被认为是最终派生对象。若 new_type 不是到构造函数/析构函数自身的类或其基类之一的指针,则行为未定义。

    同其他转型表达式,结果是:

    • 左值,若 new_type 是左值引用类型( expression 必须是左值)
    • 亡值,若 new_type 是右值引用类型( expression 为完整类类型,可以是左值或右值 (C++17 前)必须是泛左值(纯右值被实质化) (C++17 起))
    • 纯右值,若 new_type 是指针类型

    注意

    向下转型亦可用 static_cast 进行,它避免运行时检查的开销,但它仅若程序能保证(通过某些其他逻辑) expression 所指向的对象肯定是 Derived 才安全。

    运行此代码

    #include <iostream>
     
    struct V {
        virtual void f() {};  // 必须为多态以使用运行时检查的 dynamic_cast
    };
    struct A : virtual V {};
    struct B : virtual V {
      B(V* v, A* a) {
        // 构造中转型(见后述 D 的构造函数中的调用)
        dynamic_cast<B*>(v); // 良好定义: v 有类型 V* , B 的 V 基类,产生 B*
        dynamic_cast<B*>(a); // 未定义行为: a 有类型 A* , A 非 B 的基类
      }
    };
    struct D : A, B {
        D() : B((A*)this, this) { }
    };
     
    struct Base {
        virtual ~Base() {}
    };
     
    struct Derived: Base {
        virtual void name() {}
    };
     
    int main()
    {
        D d; // 最终派生类
        A& a = d; // 向上转型,可以用 dynamic_cast ,但不必须
        D& new_d = dynamic_cast<D&>(a); // 向下转型
        B& new_b = dynamic_cast<B&>(a); // 侧向转型
     
     
        Base* b1 = new Base;
        if(Derived* d = dynamic_cast<Derived*>(b1))
        {
            std::cout << "downcast from b1 to d successful\n";
            d->name(); // 调用安全
        }
     
        Base* b2 = new Derived;
        if(Derived* d = dynamic_cast<Derived*>(b2))
        {
            std::cout << "downcast from b2 to d successful\n";
            d->name(); // 调用安全
        }
     
        delete b1;
        delete b2;
    }

    输出:

    downcast from b2 to d successful

    c): const_cast 转换:在有不同 cv 限定的类型间转换。

    语法

    const_cast < new_type > ( expression )

    返回 new_type 类型的值。

    解释

    只能以 const_cast 进行下列转换。特别是,唯有 const_cast 可用于转型掉(移除)常性或易变性。

    1) 二个指向同一类型的可能多级的指针可以互相转换,无关乎每个层级的 cv 限定符。

    2) 任何 T 类型左值可转换为到同一类型 T 的左值或右值引用, cv 限定可更多或更少。同样地,右值可转换成更多或更少 cv 限定的右值引用。引用 const_cast 的结果指代原对象,若 expression 是泛左值,否则指代实质化的临时量 (C++17 起)。

    3) 同样的规则应用于可能多层的到数据成员的指针及可能多层的到已知和未知边界数组( cv 限定元素的数组被认为是自身亦有 cv 限定) (C++17 起)

    4) 空指针值可转换成 new_type 的空指针值

    同所有转型表达式,结果是:

    • 左值,若 new_type 是左值引用或到函数类型的右值引用;
    • 亡值,若 new_type 是到对象类型的右值引用;
    • 否则为纯右值。

    注意:指向函数指针和指向成员函数指针不可用于 const_cast

    const_cast 使得能够组成实际指代 const 对象 的到非 const 类型的引用或指针,或组成实际指代 volatile 对象的到非 volatile 类型的引用或指针。通过非 const 访问路径修改 const 对象和通过非 volatile 泛左值指代 volatile 对象是未定义行为。

    运行此代码

    #include <iostream>
     
    struct type {
        int i;
     
        type(): i(3) {}
     
        void f(int v) const {
            // this->i = v;                 // 编译错误: this 是指向 const 的指针
            const_cast<type*>(this)->i = v; // 只要该对象不是 const 就 OK
        }
    };
     
    int main() 
    {
        int i = 3;                 // 不声明 i 为 const
        const int& rci = i; 
        const_cast<int&>(rci) = 4; // OK :修改 i
        std::cout << "i = " << i << '\n';
     
        type t; // 假如这是 const type t ,则 t.f(4) 会是未定义行为
        t.f(4);
        std::cout << "type::i = " << t.i << '\n';
     
        const int j = 3; // 声明 j 为 const
        int* pj = const_cast<int*>(&j);
        // *pj = 4;      // 未定义行为
     
        void (type::* pmf)(int) const = &type::f; // 指向成员函数的指针
        // const_cast<void(type::*)(int)>(pmf);   // 编译错误: const_cast 不在成员函数指针上工作
    }

    输出:

    i = 4
    type::i = 4

    d): reinterpret_cast 转换:通过转译底层位模式在类型间转换。

    语法

    reinterpret_cast < new_type > ( expression )

    返回 new_type 类型的值。

    解释

    与 static_cast 不同,但与 const_cast 类似, reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针间转换,或在指针表示依赖其类型的不明架构上)。它纯粹是一个编译时指令,指示编译器将 expression 的位序列(对象表示)视为 new_type 类型的位序列。

    仅下列转换能用 reinterpret_cast 进行,除了这种转换会转型走常性易变性的情况。

    1) 整数、枚举、指针或指向成员指针类型表达式能转换到其自身的类型。产生的值与 expression 的相同。(C++11 起)

    2) 指针能转换成大到足以保有其类型所有值的任何整数类型(例如转换成 std::uintptr_t

    3) 任何整数或枚举类型能转换到指针类型。指针转换到有足够大小的整数再转换回同一指针类型后,保证拥有其原值,否则结果指针无法安全解引用(反向的来回转换不保证;相同指针可拥有多种整数表示)。空指针常量 NULL 或整数不保证生成目标类型的空指针值;为此目的应该用 static_cast隐式转换

    4) 任何 std::nullptr_t 类型值,包含 nullptr ,能转换成任何整数类型,如同它是 (void*)0 ,但没有值能转换成 std::nullptr_t ,甚至 nullptr 也不行:为该目的应该用 static_cast 。(C++11 起)

    5) 任何指向 T1 类型对象的指针能转换成指向另一类型 cv T2 对象的指针。这准确地等价于 static_cast<cv T2*>(static_cast<cv void*>(expression)) (这隐含若 T2 的对齐要求不严格于 T1 的,则指针值不改变,且结果指针转换回原类型生成原值)。任何情况下,若类型别名使用规则允许,结果指针才可以安全地解引用(见后述)

    6) T1 类型的左值表达式能转换成到 T2 类型的引用。结果是与原左值指代同一对象,但有不同类型的左值或亡值。不创建临时量,不进行复制,不调用构造函数或转换函数。若类型别名使用规则允许,结果引用才能安全访问(见后述)

    7) 任何指向函数指针能转换成指向不同函数类型的指针。通过指向相异函数类型的指针调用函数是未定义的,但转换这种指针回指向原函数的指针类型生成指向原函数的指针值。

    8) 一些实现上(特别是在任何 POSIX 兼容的系统上,因为被 dlsym 要求),函数指针能转换成 void* 或任何其他对象指针,反之亦然。若实现支持双向的转换,则转换回原类型生成原值,否则结果指针不能安全地解引用或调用。

    9) 任何指针类型的空指针值能转换成任何其他指针类型,产生该类型的空指针值。注意不能用 reinterpret_cast 转换空指针常量 nullptr 或任何其他 std::nullptr_t 类型的值为指针:为此目的应该使用隐式转换或 static_cast 。

    10) 指向成员函数的指针右值能转换成指向不同类型的不同成员函数。转换回原类型生成原值,否则结果指针不能安全使用。

    11) 指向某类 T1 成员对象的指针右值能转换成指向另一类 T2 的另一成员对象的指针。若 T2 的对齐不严格于 T1 ,则转换到原类型生成原值,否则结果指针不能安全使用。

    同所有转型表达式,结果是:

    • 左值,若 new_type 是左值引用或到函数类型的右值引用;
    • 亡值,若 new_type 是到对象类型的右值引用;
    • 否则为纯右值。

    类型别名使用

    凡在试图通过 AliasedType 类型泛左值读或修改拥有 DynamicType 类型对象的值时,行为未定义,除非下列之一为真:

    • AliasedTypeDynamicType 相似
    • AliasedTypeDynamicType 的(可有 cv 限定的)有符号或无符号变体。
    • AliasedTypestd::byte 、 (C++17 起)char 或 unsigned char :这容许检验将对象的对象表示作为字符数组检验。

    非正式地说,忽略顶层 cv 限定(但不含在函数类型内者),二个类型符合下列条件,则原类型相似

    • 它们是同一类型;或
    • 它们都是指针,而被指向的类型相似;或
    • 它们都是指向相同类的成员指针,而被指向的成员类型相似;或
    • 它们都是大小相同的数组或都是未知边界数组,而数组元素类型相似。

    例如:

    • const int * volatile * 与 int * * const 相似;
    • const int (* volatile S::* const)[20] 与 int (* const S::* volatile)[20] 相似;
    • int (* const *)(int *) 与 int (* volatile *)(int *) 相似;
    • int (S::*)() const 与 int (S::*)() 相似;
    • int (*)(int *) 与 int (*)(const int *) 相似;
    • const int (*)(int *) 与 int (*)(int *) 相似;
    • int (*)(int * const) 与 int (*)(int *) 相似(它们是同一类型);
    • std::pair<int, int> 与 std::pair<const int, int> 相似。

    此规则启用基于类型的别名分析,其中编译器假设通过一个类型的泛左值读取的值,不会为对不同类型的泛左值的写入所修改(受上述例外影响)。

    注意,许多 C++ 编译器作为非标准语言扩展放松此规则,以允许通过 union 不活跃成员的错误类型访问(这种访问在 C 中不是未定义)。

    注意:

    标准中定义严格别名使用规则的段落含有二个从 C 继承的额外条例:

    • AliasedType聚合类型union 类型,它保有作为一个作为元素或非静态成员的前述类型(递归地包含子聚合体的元素和被含有联合体的非静态数据成员)。
    • AliasedTypeDynamicType 的(可有 cv 限定的)基类

    这些条例所描述的情况不可能出现于 C++ ,从而从上面的讨论省略。 C 中,聚合复制和赋值将聚合体对象作为作为整体访问。但 C++ 中始终通过成员函数调用进行这种行动,这会访问单独的子对象,而非整个对象(或在联合体的情况下,复制对象表示,即经由 unsigned char )。见核心问题 2051

    假设符合对齐要求,则 reinterpret_cast 在处理指针可互转换对象的少数受限情况外,不更改指针的值

    struct S1 { int a; } s1;
    struct S2 { int a; private: int b; } s2; // 非标准布局
    union U { int a; double b; } u = {0};
    int arr[2];
     
    int* p1 = reinterpret_cast<int*>(&s1); // p1 的值为“指向 s1.a 的指针”
                                           // 因为 s1.a 与 s1 为指针可互转换
     
    int* p2 = reinterpret_cast<int*>(&s2); // reinterpret_cast 不更改 p2 的值为“指向 p2 的指针”。 
     
    int* p3 = reinterpret_cast<int*>(&u);  // p3 的值为“指向 u.a 的指针”: u.a 与 u 指针可互转换
     
    double* p4 = reinterpret_cast<double*>(p3); // p4 的指针为“指向 u.b 的指针”: u.a 与 u.b
                                                // 指针可互转换,因为都与 u 指针可互转换
     
    int* p5 = reinterpret_cast<int*>(&arr); // reinterpret_cast 不更改 p5 的值为“指向 arr 的指针”

    在不实际指代适当类型对象的泛左值上,进行指代非静态数据成员或非静态成员函数的成员访问——例如通过 reinterpret_cast 获得者——导致未定义行为:

    struct S { int x; };
    struct T { int x; int f(); };
    struct S1 : S {}; // 标准布局
    struct ST : S, T {}; // 非标准布局
     
    S s = {};
    auto p = reinterpret_cast<T*>(&s); // p 的值为“指向 s 的指针”
    auto i = p->x; // 类成员访问表达式为未定义行为: s 不是 T 对象
    p->x = 1; // 未定义行为
    p->f();   // 未定义行为
     
    S1 s1 = {};
    auto p1 = reinterpret_cast<S*>(&s1); // p1 的值为“指向 S 的 s1 子对象的指针”
    auto i = p1->x; // OK
    p1->x = 1; // OK
     
    ST st = {};
    auto p2 = reinterpret_cast<S*>(&st); // p2 的值为“指向 st 的指针”
    auto i = p2->x; // 未定义行为
    p2->x = 1; // 未定义行为

    许多编译器在这种情况下发布“严格别名使用”警告,即使在技术性地通过某种构造,违背异于通称为“严格别名使用规则”的段落的情况下。

    严格别名使用和相关规则的目的是启用基于类型的别名分析,若程序能合法地创建情形,使得二个指向无关类型的指针(例如一个 int* 和一个 float* )能同时存在并可一同用于加载或存储同一内存(见此 SG12 reflector 上的邮件),则别名分析会普遍无效。故任何看起来能够创建这种情形的技巧都需要引发未定义行为。

    需要转译对象的字节为不同类型的值时,能使用 std::memcpystd::bit_cast (C++20 起):

    double d = 0.1;
    std::int64_t n;
    static_assert(sizeof n == sizeof d);
    // n = *reinterpret_cast<std::int64_t*>(&d); // 未定义行为
    std::memcpy(&n, &d, sizeof d); // OK
    n = std::bit_cast<std::int64_t>(d); // 亦 OK

    缺陷报告

    下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

    DR应用于出版时的行为正确行为
    CWG 195C++98不允许函数指针和对象指针间的转换使之为条件性支持

    演示 reinterpret_cast 的一些用法:

    运行此代码

    #include <cstdint>
    #include <cassert>
    #include <iostream>
    int f() { return 42; }
    int main()
    {
        int i = 7;
     
        // 指针到整数并转回
        std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast 为错误
        std::cout << "The value of &i is 0x" << std::hex << v1 << '\n';
        int* p1 = reinterpret_cast<int*>(v1);
        assert(p1 == &i);
     
        // 到另一函数指针并转回
        void(*fp1)() = reinterpret_cast<void(*)()>(f);
        // fp1(); 未定义行为
        int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
        std::cout << std::dec << fp2() << '\n'; // 安全
     
        // 通过指针的类型别名使用
        char* p2 = reinterpret_cast<char*>(&i);
        if(p2[0] == '\x7')
            std::cout << "This system is little-endian\n";
        else
            std::cout << "This system is big-endian\n";
     
        // 通过引用的类型别名使用
        reinterpret_cast<unsigned int&>(i) = 42;
        std::cout << i << '\n';
     
        [[maybe_unused]] const int &const_iref = i;
        // int &iref = reinterpret_cast<int&>(const_iref); // 编译错误——不能去除 const
        // 必须用 const_cast 代替: int &iref = const_cast<int&>(const_iref);
    }

    可能的输出:

    The value of &i is 0x7fff352c3580
    42
    This system is little-endian
    42

    3、显式类型转换:用显式和隐式转换的组合在类型间转换。

    语法

    ( new_type ) expression(1) 
    new_type ( expression ) (2) 
    new_type ( expressions ) (3) 
    new_type ( ) (4) 
    new_type { expression-list(可选) } (5)(C++11 起)
    template ( expressions(可选) ) (6)(C++17 起)
    template { expressions(可选) } (7)(C++17 起)

    返回 new_type 类型值。

    解释

    1) 遇到 C 风格转型表达式时,编译器试图将它转译成下列转型表达式,以此顺序:

    a) const_cast<new_type>(expression);

    b) static_cast<new_type>(expression) ,带扩展:到导出类的引用或指针额外允许转型成到无歧义基类的引用或指针(反之亦然),纵使基类不可访问(即此转型忽略 private 继承指定符)。同样适用于转型指向成员指针到指向无歧义非虚基类的成员指针;

    c) static_cast (带扩展)后随 const_cast ;

    d) reinterpret_cast<new_type>(expression) ;

    e) reinterpret_cast 后随 const_cast 。

    选择首个满足各自转型运算符要求的首选项,即使它无法编译(见示例)。若转型能转译成多于一种 static_cast 后随 const_cast 的方式,则它无法编译。

    另外, C 风格转型记号允许在不完整类型的指针之间双向转型。若 expression 与 new_type 是指向不完整类型的指针,则 static_cast 还是 reinterpret_cast 得到选择是未指定的。

    2) 函数式转型表达式由一个简单类型指定符或 typedef 指定符(换言之,是单个词的类型名: unsigned int(expression) 或 int*(expression) 非法),后随括号中的单个表达式。此转型表达式准确等价于对应的 C 风格转型表达式。

    3) 若括号中有多于一个表达式,则 new_type 必须是有适合声明的构造函数的类。此表达式是 new_type 类型纯右值,其指代的临时量 (C++17 前)其结果对象 (C++17 起)以 expressions 直接初始化

    4) 若 new_type 指名一个非数组完整对象类型,则此表达式是 new_type 类型纯右值,指代该类型临时量 (C++17 前)其结果对象(可以有 cv 限定)为该类型 (C++17 起)。若 new_type 是对象类型,则对象被值初始化。若 new_type 是(可有 cv 限定的) void ,则表达式是 void 纯右值而无结果对象 (C++17 起)。

    5) 单个词的类型名后随花括号初始化器列表,是指定类型的纯右值,其指代的临时量 (C++17 前)其结果对象 (C++17 起)以指定的花括号初始化器列表直接列表初始化。这是仅有的能创建数组纯右值的表达式。

    6,7) 同 (2-5) ,除了首先进行类模板实参推导

    同所有转型表达式,结果是:

    • 左值,若 new_type 是左值引用或到函数类型的右值引用;
    • 亡值,若 new_type 是到对象类型的右值引用;
    • 否则为纯右值。

    运行此代码

    double f = 3.14;
    unsigned int n1 = (unsigned int)f; // C 风格转型
    unsigned int n2 = unsigned(f);     // 函数式转型
     
    class C1;
    class C2;
    C2* foo(C1* p)
    {
        return (C2*)p; // 转型不完整类型到不完整类型
    }
     
    // 此示例中, C 风格转型被转译成 static_cast
    // 尽管它也能作为 reinterpret_cast 工作
    struct A {};
    struct I1 : A {};
    struct I2 : A {};
    struct D : I1, I2 {};
     
    int main()
    {
        D* d = nullptr;
    //  A* a = (A*)d;                   // 编译时错误
        A* a = reinterpret_cast<A*>(d); // 这能编译
    }

    4、隐式转换

    凡是在语境中使用了某种表达式类型 T1,但语境不接受该类型,而接受另一类型 T2 的时候,会进行隐式转换,具体是:

    • 调用以 T2 为参数声明函数时,以该表达式为参数;
    • 运算符期待 T2 ,而以该表达式为运算数;
    • 初始化 T2 类型新对象,包括在返回 T2 的函数中的 return 语句;
    • 将表达式用于 switch 语句( T2 是整数类型);
    • 将表达式用于 if 语句或循环( T2 是 bool )。

    程序为良式(能编译),仅若存在一个从 T1T2 的无歧义隐式转换序列

    若有多个函数或运算符的重载会被调用,则在从 T1 到每个可用的 T2 构造隐式转化序列后,重载决议规则决定编译哪个重载。

    注意:算术表达式中,给二元运算符上的运算数上的隐式转换目标类型由一组有别的规则,通常算术转换决定。

    转换顺序

    隐式转换序列由下列内容构成,以此顺序:

    1) 零或一个标准转换序列

    2) 零或一个用户定义转换

    3) 零或一个标准转换序列

    考虑给构造函数或用户定义转换函数的参数时,只允许一个标准转换序列(否则能等效地连锁用户定义序列)。从一个内建类型转换到另一内建类型时,只允许一个标准转换序列。

    标准转换序列由下列内容构成,以此顺序:

    1) 零或一个左值变换

    2) 零或一个数值提升数值转换

    3) 零或一个函数指针转换

    (C++17 起)

    4) 零或一个限定调整

    用户定义转换由零或一个非 explicit 单实参构造函数或非 explicit 转换函数调用构成。

    若且唯若 T2 能从表达式 e 复制初始化,即对于某虚设的临时对象 t ,声明 T2 t = e; 为良式(能编译),才说表达式 e 可隐式转换为 T2 。注意这有别于直接初始化( T2 t(e) ),其中会额外考虑 explicit 构造函数和转换函数。

    按语境转换

    下列语境中,期待类型 bool ,而若声明 bool t(e); 为良式则进行隐式转换(即考虑如 explicit T::operator bool() const; 的隐式转换函数)。我们说这种表达式 e 可按语境转换为 bool

    • ifwhilefor 的控制表达式;
    • 内建逻辑运算符 !&&|| 的运算数;
    • 条件运算符 ?: 的首个运算数;
    • static_assert 声明中的谓词;
    • noexcept 指定符中的表达式;
    (C++20 起)
    (C++11 起)

    下列语境中,期待语境限定类型 T ,而类类型 E 表达式 e 仅若E 拥有单个非 explicit 用户定义转换函数以转换到可允许类型 (C++14 前)可允许类型中恰好有一个类型 T ,满足 E 拥有非 explicit 转换函数,其返回类型为(可有 cv 限定的) T 或到(可有 cv 限定的) T 的引用,且 e 可隐式转换为 T (C++14 起)才得到允许。我们说这种表达式 e 可按语境隐式转换到指定的类型 T 。注意不考虑 explicit 转换函数,即使在按语境转换到 bool 中考虑他们。 (C++11 起)

    • delete 表达式T 是任何对象指针类型);
    • 整数常量表达式,其中使用字面类( T 是任何整数或无作用域枚举类型,被选择用户定义转换函数必须是 constexpr );
    • switch 语句的控制表达式( T 是整数或枚举类型)。
    #include <cassert>
     
    template<typename T>
    class zero_init
    {
        T val;
    public:
        zero_init() : val(static_cast<T>(0)) { }
        zero_init(T val) : val(val) { }
        operator T&() { return val; }
        operator T() const { return val; }
    };
     
    int main()
    {
        zero_init<int> i; assert(i == 0);
        i = 7; assert(i == 7);
        switch(i) { }     // C++14 前错误(多于一个转换函数)
                          // C++14 (二个函数转换到同一类型 int )
        switch(i + 0) { } // 始终 OK (隐式转换)
    }

    值变换

    值变换是更改表达式值类别的转换。凡是表达式作为期待不同值类别表达式的运算符的运算数时,值变换发生。

    左值到右值转换

    任何非函数、非数组类型 T泛左值能隐式转换成同类型的纯右值。若 T 为非类类型,则此转换亦移除 cv 限定符。若泛左值拥有 std::nullptr_t 类型,则作为结果的纯右值是空指针常量 nullptr 。

    除非遇到不求值语境( sizeof 、 typeid 、 noexcept 或 decltype 的运算数中),此转换等效地以原泛左值为构造函数参数,复制构造 T 临时对象,然后将该临时对象作为纯右值返回。

    此转换模仿从内存位置读取值到 CPU 寄存器中的行动。

    若泛左值所指代的对象含有不确定值(例如由默认初始化非类类型静态变量获得),则行为未定义

    除非不确定值拥有可为 cv 限定的无符号字符类型,且不缓存于 CPU 寄存器,或者正式而言:

    • 存储期是静态或线程局域;
    • 或已构造指向它的指针;
    • 或已将它绑定到引用。

    若泛左值含有为 delete 所非法化的指针值,则行为亦为实现定义(而非未定义)。

    (C++11 起)

    数组到指针转换

    NT 的数组”或“ T 的未知边界数组”类型的左值右值能隐式转换为“指向 T 的指针”类型的纯右值。 若数组是纯右值,则发生临时量实质化。 (C++17 起)产生的指针指向数组首元素(细节参阅数组到指针退化)。

    临时量实质化

    任何完整类型 T纯右值能转换为同类型 T 的亡值。此转换从纯右值初始化 T 类型临时对象,通过以临时对象为结果对象求值该纯右值。

    T 是类类型或类类型数组,则它必须有可访问且未被删除的析构函数

    struct S { int m; };
    int k = S().m; // C++17 起成员访问期待泛左值;
                   // 转换 S() 纯右值为亡值

    临时量实质化在下例情形出现:

    (C++17 起)

    函数到指针

    函数类型 T左值能隐式转换成指向该函数的指针纯右值。这不作用于非静态成员函数,因为不存在指代非静态成员函数的左值。

    数值提升

    整数类型提升

    小整数类型(如 char )的纯右值能转换为较大整数类型(如 int )的纯右值。具体而言,算术运算符不接受小于 int 的类型为参数,而在左值到右值转换后自动应用整数提升,若它可应用。此转换始终保持原值。

    以下类型转换被分类为是整数类型提升:

    • signed charsigned short 能转换为 int ;
    • unsigned charunsigned short 能转换为 int ,若 int 能保有其整个值范围,否则能转换为 unsigned int ;
    • char 能转换为 int 或 unsigned int ,取决于底层类型: signed char 或 unsigned char (见上述);
    • wchar_tchar16_tchar32_t 能转换为以下列表中能保有其整个值范围的首个类型: int 、 unsigned int 、 long 、 unsigned long 、 long long 、 unsigned long long ;
    • 底层类型不固定的无作用域枚举类型能转换为以下列表中能保有其整个值范围的首个类型: int 、 unsigned int 、 long 、 unsigned long 、 long long 、 unsigned long long 、扩展整数类型(以大小顺序,有符号优先于无符号) (C++11 起)。若值范围更大,则不应用整数类型提升;
    • 底层类型固定的无作用域枚举类型能转换为其底层类型,而若底层类型亦可进行整数类型提升,则还有提升后的底层类型。到未提升的底层类型的转换对于重载决议的目的更优;
    (C++11 起)
    • 位域类型能转换为 int ,若 int 能表示位域的整个值范围,否则能转换为 unsigned int ,若 unsigned int 能表示位域的整个值范围,否则不应用整数类型提升;
    • bool 类型能转换为 int ,值 false 变为 ​0​ 而 true 变为 1 。

    注意所有其他转换都不是提升;例如重载决议选择 char -> int (提升)优先于 char -> short (转换)。

    浮点类型提升

    float 类型纯右值能转换为 double 类型值。值不更改。

    数值转换

    不同于提升,数值转换可以更改值,而且有潜在的精度损失。

    整数类型转换

    任何整数类型或无作用域枚举类型的纯右值能隐式转换成任何其他整数类型。若转换列在整数类型提升下,则它是提升而非转换。

    • 若目标类型为无符号,则结果值是等于源值 2n
      的最小无符号值,其中 n 是用于表示目标类型的位数。

    即取决于目标类型更宽或更窄,分别符号扩展[脚注 1]或截断有符号数,而零扩展或截断无符号数。

    • 若目标类型有符号,则若源整数能以目标类型表示,则不更改其值。否则结果是实现定义的(注意这异于未定义的有符号整数算术溢出)。
    • 若源类型为 bool ,则值 false 转换为目标类型的零,而值 true 转换成目标类型的一(注意若目标类型为 int ,则这是整数类型提升,而非整数类型转换)。
    • 若目标类型为 bool ,则这是布尔转换(见后述)。

    浮点类型转换

    浮点类型纯右值能转换成任何其他浮点类型的纯右值。若转换列于浮点类型提升下,则它是提升而非转换。

    • 若源值能以目标类型准确表示,则不更改它。
    • 若原值在目标类型的二个可表示值之间,则结果是二个值之一(选择哪个是实现定义的,不过若支持 IEEE ,则舍入默认为到最接近)。
    • 否则,行为未定义。

    浮点整数转换

    • 浮点类型纯右值能隐式转换成任何整数类型的纯右值。截断小数部分,即舍弃小数部分。若结果不能适应到目标类型中,则行为未定义(即使在目标类型为无符号数时,也不应用模算术)。若目标类型为 bool ,则这是布尔转换(见后述)。
    • 无作用域枚举类型或整数类型的纯右值能转换成任何浮点类型的纯右值。若不能正确表示该值,则选择最接近较高值还是最接近的较低值是实现定义的,不过若支持 IEEE ,则舍入默认为到最接近。若值不能适应到目标类型中,则行为未定义。若源类型为 bool ,则值 false 转换为零,而值 true 转换为一。

    指针转换

    • 空指针常量(见 NULL )能转换成任何指针类型,而结果是该类型的空指针值。允许这种转换(称为空指针转换) 作为单次转换,转换到 cv 限定类型,即不认为它是数值和限定转换的结合。
    • 指向任何(可有 cv 限定的)对象类型 T 的指针纯右值能转换成指向(有等同 cv 限定的) void 的指针纯右值。结果指针与原指针表示内存中的同一位置。若原指针是空指针值,则结果为目标类型的空指针值。
    • 指向派生类类型的(可有 cv 限定的)空指针能转换成指向其(有等同 cv 限定的)基类的指针。若基类不可访问或有歧义,则转换为病式(不能编译)。转换结果是指向原被指向对象内的基类子对象的指针。空指针值转换成目标类型的空指针值。

    成员指针转换

    • 空指针常量(见 NULL )能转换成任何指向成员指针类型,而结果是该类型的空成员指针值。允许这种转换(成为空成员指针转换)作为单次转换,转换到 cv 限定类型,即不认为它是数值和限定转换的结合。
    • 指向基类 B 中某类型 T 成员的指针纯右值能转换成指向其派生类 D 中同一类型 T 成员的指针纯右值。若 BD 的间接、有歧义或虚基类或是 D 的某个中间虚基类的基类,则转换为病式(不能编译)。能以 D 对象解引用结果指针,而它将访问该 D 对象的 B 基类子对象内的成员。空成员指针值转换成目标类型的空成员指针值。

    布尔转换

    整数、浮点、无作用域枚举、指针和指向成员指针类型的纯右值能转换成 bool 类型纯右值。

    值零(对于整数、浮点和无作用域枚举)、空指针值和空成员指针值变为 false 。所有其他值变为 true 。

    std::nullptr_t 类型纯右值,包括 nullptr ,能在直接初始化的语境中转换成 bool 类型纯右值。结果值为 false 。

    (C++11 起)

    限定转换

    • 指向有 cv 限定的类型 T 的指针类型纯右值能转换为指向有更多 cv 限定的同一类型 T 的指针纯右值(换言之,能添加常性和易变性)。
    • 指向类 X 中有 cv 限定的类型 T 的指向成员指针纯右值能转换成指向类 X 中有更多 cv 限定的类型 T 的指向成员指针纯右值。

    “更多” cv 限定表明

    • 指向无限定类型的指针能转换成指向 const 的指针;
    • 指向无限定类型的指针能转换成指向 volatile 的指针;
    • 指向无限定类型的指针能转换成指向 const volatile 的指针;
    • 指向 const 类型的指针能转换成指向 const volatile 的指针;
    • 指向 volatile 类型的指针能转换成指向 const volatile 的指针。

    对于多级指针,应用下列限制:身为 cv1
    0 限定指针,指向 cv1
    1 限定指针,指向…… cv1
    n-1 限定指针,指向 cv1
    n 限定 T 的多级指针 P1 ,可转换成身为 cv2
    0 限定指针,指向 cv2
    1 限定指针,指向…… cv2
    n-1 限定指针,指向 cv2
    n 限定 T 的多级指针 P2 ,仅若

    • 两个指针的级数 n 相同;
    • 若在 P1 的某级(除了零级)的 cv1
      k 中有 const ,则在 P2 的同级 cv2
      k 中有 const ;
    • 若在 P1 的某级(除了零级)的 cv1
      k 中有 volatile ,则在 P2 的同级 cv2
      k 中有 volatile ;
    • 若在某级 k 上, P2P1更多 cv 限定,则 P2k 为止的每一级(除了零级) cv2
      1, cv2
      2 ... cv2
      k 上都必须有 const 。
    • 同样的规则用于指向成员的多级指针及指向对象和指向成员的多级混合指针;
    • 同样的规则用于包含在任何级指向已知边界或未知边界数组(认为有 cv 限定元素的数组自身有等同的 cv 限定)的多级指针;
    (C++14 起)
    • 零级由非多级限定转换的规则处理。
    char** p = 0;
    const char** p1 = p; // 错误: 2 级有更多 cv 限定但 1 级非 const
    const char* const * p2 = p; // OK : 2 级有更多 cv 限定并在 1 级添加 const
    volatile char * const * p3 = p; // OK : 2 级更有 cv 限定并在 1 级添加 const
    volatile const char* const* p4 = p2; // OK : 2 级更有 cv 限定而 const 已在 1 级
     
    double *a[2][3];
    double const * const (*ap)[3] = a; // C++14 起 OK

    注意 C 编程语言中,只能添加 const/volatile 到第一级:

    char** p = 0;
    char * const* p1 = p; // C 与 C++ 中 OK
    const char* const * p2 = p; // C 中错误, C++ 中 OK

    函数指针转换

    • 指向不抛出函数的指针类型的纯右值能转换成指向潜在抛出函数的指针纯右值。
    • 指向不抛出成员函数指针类型的纯右值能转换成指向潜在抛出函数指针的纯右值。
    void (*p)();
    void (**pp)() noexcept = &p; // 错误:不能转换成指向 noexcept 函数的指针
     
    struct S
    {
        typedef void (*p)();
        operator p();
    };
    void (*q)() noexcept = S(); // 错误:不能转换成指向 noexcept 函数的指针
    (C++17 起)

    安全 bool 问题

    在 C++11 引入显式转换函数之前,设计一个能用于布尔语境的类(比如, if(obj) { ... } )会出现问题:给定用户定义转换函数,如 T::operator bool() const; ,则隐式转换序列允许再多一步标准转换序列,也就是 bool 结果会转换成 int ,允许诸如 obj << 1; 或 int i = obj; 的代码。

    一个早期的解决方案可参见 std::basic_ios ,它定义 operator! 和 operator void*(C++11 前),使得如 if(std::cin) {...} 的代码能编译,因为 void* 能转换为 bool ,但int n = std::cout; 不能,因为 void* 不可转换至 int 。这仍然允许无意义代码能编译,如 delete std::cout; 。许多 C++11 前的第三方库设计带有更为复杂的解决方案,称作安全 Bool 手法

    显式 bool 转换亦能用于解决安全 bool 问题

    explicit operator bool() const { ... }
    (C++11 起)

    脚注

    1. 仅若算术为补码才使用,仅对定宽整数类型要求补码。然而注意目前所有拥有 C++ 编译器的平台都使用补码算术。

    缺陷报告

    下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

    DR应用于出版时的行为正确行为
    CWG 616C++11任何未初始化对象的左值到右值的转换是未定义行为允许不定值的 unsigned char
    CWG 1423C++11std::nullptr_t 在直接或复制初始化中可转换为 bool只允许直接初始化
    CWG 330C++14从 double * const (*p)[3] 到 double const * const (*p)[3] 的转换非法转换合法

    5、用户定义转换:允许从类类型到其他类型的隐式转换显式转换

    语法

    转换函数声明类似非静态成员函数函数模板,而无参数或显式返回类型,并拥有下列形式的名称:

    operator conversion-type-id(1) 
    explicit operator conversion-type-id(2)(C++11 起)

    1) 声明用户定义的转换函数,它参与所有隐式显式转换

    2) 声明用户定义的转换函数,它仅参与直接初始化显式转换

    conversion-type-id 是一个类型 id ,除了函数与数组运算符 []() 不允许出现于其声明器中(从而转换到诸如指向数组指针的类型要求类型别名/ typedef 或标识模板:见后述)。无关乎 typedef , conversion-type-id 不能表示数组或函数类型。

    尽管返回类型不允许出现于用户定义转换函数的声明中,声明文法的 decl-specifier-seq 可以存在并可包含任何异于 type-specifier 或关键词 static 的指定符。尤其是在 explicit 外,亦允许指定符 inlinevirtualconstexprfriend (注意 friend 要求有限定名: friend A::operator B(); )。

    类 X 中声明这种成员函数时它进行从 X 到 conversion-type-id 的转换:

    struct X {
        // 隐式转换
        operator int() const { return 7; }
     
        // 显式转换
        explicit operator int*() const { return nullptr; }
     
    //   错误:数组运算符不允许出现于 conversion-type-id 中
    //   operator int(*)[3]() const { return nullptr; }
        using arr_t = int[3];
        operator arr_t*() const { return nullptr; } // OK 若通过 typedef 进行
    //  operator arr_t () const; // 错误:不允许任何情况下转换到数组
    };
     
    int main()
    {
        X x;
     
        int n = static_cast<int>(x);   // OK :设 n 为 7
        int m = x;                     // OK :设 m 为 7
     
        int* p = static_cast<int*>(x);  // OK :设 p 为 null
    //  int* q = x; // 错误:无隐式转换
     
        int (*pa)[3] = x;  // OK
    }

    解释

    用户定义的转换函数在隐式转换的第二阶段被调用,第二阶段由零或一个转换构造函数或零或一个用户定义转换函数构成。

    若转换函数和转换构造函数都能用于进行用户定义转换,则转换函数和转换构造函数都为复制初始化引用初始化语境中的重载决议所考虑,但在直接初始化语境中只考虑转换构造函数。

    struct To {
        To() = default;
        To(const struct From&) {} // 转换构造函数
    };
     
    struct From {
        operator To() const {return To();} // 转换函数
    };
     
    int main()
    {
        From f;
        To t1(f); // 直接初始化:调用构造函数
    // (注意,若转换构造函数不可用,则选择隐式复制构造函数,且调用转换函数以准备其参数)
        To t2 = f; // 复制初始化:歧义
    // (注意,若转换函数来自非 const 类型,例如 From::operator To(); 
    //  则它在此情况中替代构造函数被选中)
        To t3 = static_cast<To>(f); // 直接初始化:调用构造函数
        const To& r = f; // 引用初始化:歧义
    }

    到其自身(可有 cv 限定)类(或到其引用),和到类型 void 的转换函数可以定义,但无法作为转换序列的一部分执行,除非在某些情况下通过派发:

    struct D;
    struct B {
        virtual operator D() = 0;
    };
    struct D : B
    {
        operator D() override { return D(); }
    };
     
    int main()
    {
        D obj;
        D obj2 = obj; // 不调用 D::operator D()
        B& br = obj;
        D obj3 = br; // 通过虚派发调用 D::operator D() 
    }

    它亦可用成员函数调用语法调用:

    struct B {};
    struct X : B {
        operator B&() { return *this; };
    };
     
    int main()
    {
        X x;
        B& b1 = x;                  // 不调用 X::operatorB&()
        B& b2 = static_cast<B&>(x); // 不调用 X::operatorB&
        B& b3 = x.operator B&();    // 调用 X::operatorB&
    }

    显式调用转换函数时,类型 id 是贪心的:它是作为合法类型 id 的最长可能记号序列(包括属性,若存在):

    & x.operator int * a; // 分析为 & (x.operator int*) a
                          // 而非 & (x.operator int) * a

    占位符 auto 可用于 conversion-type-id ,指示推导的返回类型

    struct X {
        operator int(); // OK
        operator auto() -> short;  // 错误:尾随返回类型不是语法的一部分
        operator auto() const { return 10; } // OK :推导的返回类型
    };

    注意:转换函数模板不允许拥有推导的返回类型。

    (C++14 起)

    转换函数可以继承而且可以为,但不能为静态。导出类的转换函数不会隐藏基类中的转换函数,除非它们转换到同一类型。

    转换函数可以为模板成员函数,例如 std::auto_ptr<T>::operator auto_ptr<Y> 。可应用的特殊规则参阅成员模板模板实参推导

    6、运行时类型信息

    a): std::bad_typeid

    定义于头文件 <typeinfo>

      

    class bad_typeid : public std::exception;

      

    此类型的异常在应用 typeid 运算符到多态类型的空指针值时抛出。

    std-bad typeid-inheritance.svg

                           继承图

    成员函数

    (构造函数)

    构造新的 bad_typeid 对象
    (公开成员函数)

    继承自 std::exception

    成员函数

    (析构函数)

    [虚]

    析构该异常对象
    (std::exception 的虚公开成员函数) 

    what

    [虚]

    返回解释性字符串
    (std::exception 的虚公开成员函数) 

    运行此代码

    #include <iostream>
    #include <typeinfo>
     
    struct S { // 类型必须是多态
        virtual void f();
    }; 
     
    int main()
    {
        S* p = nullptr;
        try {
            std::cout << typeid(*p).name() << '\n';
        } catch(const std::bad_typeid& e) {
            std::cout << e.what() << '\n';
        }
    }

    输出:

    Attempted a typeid of NULL pointer!

    b): std::type_info

    定义于头文件 <typeinfo>

      

    class type_info;

      

    type_info 保有一个类型的实现指定信息,包括类型的名称和比较二个类型相等的方法或相对顺序。这是 typeid 运算符所返回的类。

    type_info 既非可复制构造 (CopyConstructible) 亦非可复制赋值 (CopyAssignable) 。

    成员函数

    (构造函数)

    [被删除]

    无默认或复制构造函数
    (公开成员函数)

    (析构函数)

    [虚]

    通过指向基类的指针删除导出对象是安全的
    (虚公开成员函数)

    operator=

    [被删除]

    不能复制赋值
    (公开成员函数)

    operator==
    operator!=

    检查对象是否指代相同类型
    (公开成员函数)

    before

    检查在实现定义的顺序中,被指代类型是否在另一个 type_index 对象之前,即对被指代类型排序
    (公开成员函数)

    hash_code

    (C++11)

    返回对于同一类型相同的值
    (公开成员函数)

    name

    类型的实现定义名称
    (公开成员函数)

    参阅

    type_index

    (C++11)

    针对 type_info 对象的包装,它能用作关联容器和无序关联容器的索引。
    (类)

    c): std::bad_cast

    定义于头文件 <typeinfo>

      

    class bad_cast : public std::exception;

      

    dynamic_cast 对引用类型运行时检查失败(例如因为类型并非以继承关联)时,还有若请求的平面不存在于本地环境时从 std::use_facet 抛出此类型异常。

    std-bad cast-inheritance.svg

                       继承图

    成员函数

    (构造函数)

    构造新的 bad_cast 对象
    (公开成员函数)

    继承自 std::exception

    成员函数

    (析构函数)

    [虚]

    析构该异常对象
    (std::exception 的虚公开成员函数)

    what

    [虚]

    返回解释性字符串
    (std::exception 的虚公开成员函数)

    示例

    运行此代码

    #include <iostream>
    #include <typeinfo>
     
    struct Foo { virtual ~Foo() {} };
    struct Bar { virtual ~Bar() {} };
     
    int main()
    {
        Bar b;
        try {
            Foo& f = dynamic_cast<Foo&>(b);
        } catch(const std::bad_cast& e)
        {
            std::cout << e.what() << '\n';
        }
    }

    可能的输出:

    Bad dynamic cast

    d): std::type_index

    定义于头文件 <typeindex>

      

    class type_index;

     (C++11 起)

    type_index 类是一个围绕 std::type_info 的包装类,它可用作关联与无序关联容器的索引。它与 type_info 对象的关系通过一个指针维系,故而 type_index 可复制构造 (CopyConstructible) 且可复制赋值 (CopyAssignable) 。

    成员函数

    (构造函数)

    构造对象
    (公开成员函数) [编辑]

    (析构函数)

    (隐式声明)

    销毁 type_index 对象
    (公开成员函数)

    operator=

    (隐式声明)

    type_index 对象赋值
    (公开成员函数)

    operator==
    operator!=
    operator<
    operator<=
    operator>
    operator>=

    比较底层 std::type_info 对象
    (公开成员函数) [编辑]

    hash_code

    返回哈希码
    (公开成员函数) [编辑]

    name

    返回关联到底层 type_info 对象的实现定义名称
    (公开成员函数) [编辑]

    辅助类

    std::hash<std::type_index>

    (C++11)

    std::type_index 的哈希支持
    (类模板特化) [编辑]

    下面的程序是一个有效的类型-值映射的示例。

    运行此代码

    #include <iostream>
    #include <typeinfo>
    #include <typeindex>
    #include <unordered_map>
    #include <string>
    #include <memory>
     
    struct A {
        virtual ~A() {}
    };
     
    struct B : A {};
    struct C : A {};
     
    int main()
    {
        std::unordered_map<std::type_index, std::string> type_names;
     
        type_names[std::type_index(typeid(int))] = "int";
        type_names[std::type_index(typeid(double))] = "double";
        type_names[std::type_index(typeid(A))] = "A";
        type_names[std::type_index(typeid(B))] = "B";
        type_names[std::type_index(typeid(C))] = "C";
     
        int i;
        double d;
        A a;
     
        // 注意我们正在存储指向类型 A 的指针
        std::unique_ptr<A> b(new B);
        std::unique_ptr<A> c(new C);
     
        std::cout << "i is " << type_names[std::type_index(typeid(i))] << '\n';
        std::cout << "d is " << type_names[std::type_index(typeid(d))] << '\n';
        std::cout << "a is " << type_names[std::type_index(typeid(a))] << '\n';
        std::cout << "b is " << type_names[std::type_index(typeid(*b))] << '\n';
        std::cout << "c is " << type_names[std::type_index(typeid(*c))] << '\n';
    }

    输出:

    i is int
    d is double
    a is A
    b is B
    c is C

    参考:

    https://zh.cppreference.com/w/cpp

    展开全文
  • C++中四种强制类型转换(也叫显式转换)是:static_cast, dynamic_cast, const_cast, reinterpret_cast 形式 cast_name<type>expression const_cast ,类型必须是指针,引用或者指向对象类的指针,去掉底层...

    C++中四种强制类型转换(也叫显式转换)是:static_cast, dynamic_cast, const_cast, reinterpret_cast
    形式
    cast_name<type>expression

    1. const_cast ,类型必须是指针,引用或者指向对象类的指针,去掉底层运算对象的const,这样就能进行写操作,只有const_cast能够改变表达式的常量属性,使用其他类型的强制转换都会引发编译器错误
    2. static_cast 用于各种非多态类型的转换,比如非 const 转 const,void*转指针等, static_cast 能用于多态向上转化,如果向下转能成功但是不安全,结果未知但是对于底层指针的const相互转换不行
    3. dynamic_cast 用于多态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指 针或引用。向下转化时,如果是非法的对于指针返回 NULL,对于引用抛异常。要深入了解内部 转换的原理。
      向上转换:指的是子类向基类的转换
      向下转换:指的是基类向子类的转换
      它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否 能够进行向下转换。
    4. reinterpret_cast 几乎什么都可以转,比如将 int 转指针,非常危险,必须对编译器的各种转换非常了解。
    5. 为什么不使用 C ++的强制转换? C++ 的强制转换表面上看起来功能强大什么都能转,但是转化不够明确,干扰了正常的类型检查, 容易出错

    关于C++里面的类型转换建议看:
    https://www.cplusplus.com/doc/tutorial/typecasting/
    强制类型转换:
    https://docs.microsoft.com/en-us/cpp/cpp/casting-operators?redirectedfrom=MSDN&view=msvc-170
    参考资料:
    C++ Primer 5th Edition

    展开全文
  • 使用标准C++类型转换符:static_cast、dynamic_cast、reinterpret_cast和const_cast。 const_cast,字面上理解就是去const属性。 static_cast,命名上理解是静态类型转换。如int转换成char。 dynamic_cast,...
  • 本篇文章对C++中的强制类型转换进行了详细的分析介绍。需要的朋友参考下
  • 在C语言中,强制类型转换的方式为(Type)Expression,...然而,强制类型转换在实际工程中几乎是不可避免的,为此C++强制类型转换分为4种不同的类型,以提供更加安全可靠的转换。 强制类型转换 说 明 static_cast
  • 11- C++强制类型转换

    2017-06-16 13:39:25
    C和C++强制类型转换
  • c++强制类型转换

    2019-05-17 14:51:42
    c++中static_cast和dynamic_cast强制类型转换 1. C++中层次类型转换中无非两种:上行转换和下行转换 对于上行转换,static_cast和dynamic_cast效果一样,都安全; 对于下行转换:你必须确定要转换的数据确实是...
  • c++与c的类型转换c与c++类型转换的比较c++强制类型转换 c与c++类型转换的比较 c风格的类型转换一般是直接使用基本内置类型进行转换,c++中定义了4个关键字:static_cast、dynamic_cast、reinpreter_cast、const_...
  • C++四种强制类型转换

    2020-12-22 22:25:11
    四种强制类型转换:static_cast const_cast dynamic_cast reinterpret_cast  1、static_cast  编译器隐式执行的任何类型转换都可以由static_cast显示完成。  ①用于类层次结构中基类和子类之间指针或引用的...
  • 深入理解C++强制类型转换

    千次阅读 2017-05-07 20:46:30
    C++四种强制转换类型 static_cast reinterpret_cast const_cast dynamic_cast static_cast 静态转换,用于非多态类型,任何标准的转换都可以用它,不能用于两个互不相干的类型 可以理解为能...
  • C++强制类型转换操作符 const_cast

    千次阅读 2019-03-29 21:18:06
    const_cast也是一个强制类型转换操作符。《C++ Primer》中是这样描述它的: 1.将转换掉表达式的const性质。 2.只有使用const_cast才能将const性质性质转化掉。试图使用其他三种形式的强制转换都会导致编译时的错误...
  • C++ 四种强制类型转换的总结
  • 一说起强制类型转换大家都很熟悉,相信很多学习完C++的朋友还在使用C语言的强制类型的方式 (类型)变量. C++其实也具有自己的一套强制类型转换它们分明是:static_cast reinterpret_cast const_cast dynamic_cast...
  • C++】四种强制类型转换

    万次阅读 多人点赞 2019-02-28 21:05:08
    C++ 四种强制类型转换   C语言中的强制类型转换(Type Cast)有显式和隐式两种,显式一般就是直接用小括号强制转换,TYPE b = (TYPE)a; 隐式就是直接 float b = 0.5; int a = b; 这样隐式截断(by the way 这样...
  • C和C++强制类型转换

    千次阅读 2020-10-18 16:29:51
    C++ 四种强制类型转换 一般来说,我们需要类型转换的场景可以分为如下几种: 整型和浮点型以及不同长度的数据相互转换 一般规则是:占用内存低的向占用内存高的自动转换,而反向则会有转换截断的问题 整型和指针...
  • C++ 四种强制类型转换

    千次阅读 2019-08-28 22:07:37
    C++ 类型转换(C风格的强制转换): 在C++基本的数据类型中,可以分为四类:整型,浮点型,字符型,布尔型。其中数值型包括 整型与浮点型;字符型即为char。 (1)将浮点型数据赋值给整型变量时,舍弃其小数部分。...
  • C++ 强制类型转换和赋值中的类型转换

    万次阅读 多人点赞 2017-02-02 12:11:45
    强制类型转换原C语言的形式:(类型名)(表达式)比如:(double)a //把a转换为double类型 (int)(a+b) //把a+b的值转换为整型需要注意的是:如果强制类型转换的对象是一个变量,那么该变量不需要用括号括起来;但是如果...
  • C++四种强制类型转换运算符

    千次阅读 2020-02-29 12:03:13
    C/C++中的类型转换分为两种:隐式类型转换和显式类型转换。 而对于隐式类型转换,在很多时候,不经意间就发生了,比如int类型和float类型赋值时,int类型就会被隐式的转换位float类型。 举例: int i = 5; float d =...
  • 深入理解C++中五种强制类型转换的使用场景

    千次阅读 多人点赞 2021-07-02 22:15:48
    C++中五种强制类型转换解析1、C风格的强制类型转换2、C++风格的强制类型转换2.1、static_cast2.1.1、类实例转换场景使用static_cast2.1.2、没有多态的场景下使用static_cast2.1.3、具有多态的场景下使用static_cast...
  • C++的表达式中,不同的数据类型会自动地转换类型进行运算,但有的时候也需要程序员自己进行强制类型转换,将某个表达式转换成自己所需要的数据类型。 强制类型转换的一般形式为 (类型名) (表达式) 如果进行强制...
  • C++ float强制类型转换需注意的小Bug 假设现在有这样一个需求,将double类型的数据转成float,由于C++并不会执行隐式类型转换,因此一般的做法是用float强制转换。 示例如下: float b = (float) a; //a是double类型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,495
精华内容 46,998
关键字:

c++强制类型转换

c++ 订阅