命名空间
变体
操作

显式类型转换

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
 
 

使用显式和隐式转换的组合在类型之间进行转换。

目录

[编辑] 语法

( 类型标识符 ) 一元表达式 (1)
简单类型说明符 ( 表达式列表 (可选) )
简单类型说明符 ( 初始化列表 (可选) )
(2) (C++11 前)
(C++11 起)
简单类型说明符 { 初始化列表 (可选) } (3) (C++11 起)
简单类型说明符 { 指定初始化列表 } (4) (C++20 起)
typename 标识符 ( 初始化列表 (可选) ) (5) (C++11 起)
typename 标识符 { 初始化列表 (可选) } (6) (C++11 起)
typename 标识符 { 指定初始化列表 } (7) (C++20 起)

显式地将任意数量的值转换为目标类型的值。

1) 显式类型转换(C 风格类型转换)。
2-7) 显式类型转换(函数风格类型转换)。
类型标识 - 一个 类型标识符
一元表达式 - 一个一元表达式(其顶层运算符的优先级不高于 C 风格类型转换)
简单类型说明符 - 一个简单类型说明符
表达式-列表 - 以逗号分隔的表达式列表(除了不带括号的逗号表达式
初始化器-列表 - 以逗号分隔的初始化子句列表
指定初始化列表 - 以逗号分隔的指定初始化子句列表
标识符 - 一个(可能带有限定符的)标识符(包括模板标识符

[编辑] 解释

1) 当遇到 C 风格类型转换时,编译器会尝试按以下顺序将其解释为以下类型转换表达式:
a) const_cast<类型标识符 >(一元表达式 )
b) static_cast<类型标识符 >(一元表达式 ),并扩展:指向派生类的指针或引用额外允许被转换为指向无歧义基类的指针或引用(反之亦然),即使该基类是不可访问的(即,此转换忽略私有继承说明符)。同样适用于将成员指针转换为指向无歧义非虚基类成员的指针;
c) 一个 static_cast(带扩展)后跟一个 const_cast
d) reinterpret_cast<类型标识符 >(一元表达式 )
e) 一个 reinterpret_cast 后跟一个 const_cast
即使第一个选择是格式错误的,也会选择满足相应类型转换运算符要求的第一个选择(参见示例)。如果使用 static_cast 后跟 const_cast,并且该转换可以以多种方式解释,则该转换是格式错误的。
此外,C 风格的类型转换可以在指向不完整类类型的指针之间进行转换。如果 类型标识符一元表达式 的类型都是指向不完整类类型的指针,则未指定选择 static_cast 还是 reinterpret_cast
2-7) 函数风格的类型转换指定一个类型简单类型说明符 标识符 (C++11 起))和一个初始化器(其余部分),它构造一个目标类型 T 的值,该类型由指定的类型和初始化器(C++17 起)确定。

T 是指定的类型。

(C++17 前)

T 的确定方式如下:

  • 如果指定的类型是推导类类型的占位符,则 T 是通过类模板推导的重载解析选择的函数的返回类型。
  • 否则,如果指定的类型包含占位符类型,则 T 是推导出的类型。
(C++23 起)
  • 否则,T 是指定的类型。
(C++17 起)
转换结果的确定方式如下:
  • 如果函数风格类型转换的语法为 (2),并且括号中只有一个表达式,则此类型转换等同于相应的 C 风格类型转换。
  • 否则,如果 T 是(可能带有 cv 限定符的)void,则结果是类型 void一个右值(C++11 前)一个纯右值(C++11 起),不执行初始化。
  • 如果初始化器不是 (),则程序格式错误。
(C++11 前)
  • 如果初始化器在包扩展(如果有)之后不是 (){},则程序格式错误。
(C++11 起)
  • 否则,如果 T 是引用类型,则函数风格类型转换与从指定初始化器直接初始化一个虚构的类型 T 的变量 t 具有相同的效果,结果是被初始化的 t
  • 结果是一个左值。
(C++11 前)
  • 如果 T 是左值引用类型或函数类型的右值引用,则结果是左值。
  • 否则,结果是亡值。
(C++11 起)
  • 否则,结果是类型 T一个右值(C++11 前)一个纯右值(C++11 起)指代一个临时对象(C++17 前)其结果对象(C++17 起)使用指定初始化器直接初始化

[编辑] 歧义解析

[编辑] 歧义声明语句

在带有函数风格类型转换表达式作为最左侧子表达式的表达式语句与声明语句之间存在歧义的情况下,通过将其视为声明来解决歧义。这种消歧义纯粹是语法上的:它不考虑语句中出现的名称的含义,除了它们是否是类型名称

struct M {};
struct L { L(M&); };
 
M n;
void f()
{
    M(m);    // declaration, equivalent to M m;
    L(n);    // ill-formed declaration, equivalent to L n;
    L(l)(m); // still a declaration, equivalent to L l((m));
}

然而,如果歧义声明语句中最外层的声明符具有尾随返回类型,则该语句仅在尾随返回类型以 auto 开头时才被视为声明语句。

struct M;
 
struct S
{
    S* operator()();
    int N;
    int M;
 
    void mem(S s)
    {
        auto(s)()->M; // expression (S::M hides ::M), invalid before C++23
    }
};
 
void f(S s)
{
    {
        auto(s)()->N; // expression, invalid before C++23
        auto(s)()->M; // function declaration, equivalent to M s();
    }
    {
        S(s)()->N;    // expression
        S(s)()->M;    // expression
    }
}
(C++11 起)

[编辑] 歧义函数参数

上述歧义也可能发生在声明上下文中。在这种上下文中,选择是在具有函数风格类型转换作为初始化器的对象声明与涉及函数声明符的声明之间,其中参数名称周围有一组多余的括号。解决方案也是将任何可能作为声明的构造(例如潜在的参数声明)视为声明。

struct S
{
    S(int);
};
 
void foo(double a)
{
    S w(int(a)); // function declaration: has a parameter `a` of type int
    S x(int());  // function declaration: has an unnamed parameter of type int(*)() 
                 // that is adjusted from int()
 
    // Ways to avoid ambiguity:
    S y((int(a))); // object declaration: extra pair of parentheses
    S y((int)a);   // object declaration: C-style cast
    S z = int(a);  // object declaration: no ambiguity for this syntax
}

然而,如果歧义参数声明中最外层的声明符具有尾随返回类型,则只有当它以 auto 开头时,歧义才会被解决为声明。

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    S a(B()->C);    // object declaration: B()->C cannot declare a parameter
    S b(auto()->C); // function declaration: has an unnamed parameter of type C(*)()
                    // that is adjusted from C()
}
(C++11 起)

[编辑] 歧义类型标识符

函数风格类型转换与类型标识符之间的相似性可能导致歧义。解决方案是任何在其语法上下文中可能作为类型标识符的构造都应被视为类型标识符。

// `int()` and `int(unsigned(a))` can both be parsed as type-id:
// `int()`            represents a function returning int
//                    and taking no argument
// `int(unsigned(a))` represents a function returning int
//                    and taking an argument of type unsigned
void foo(signed char a)
{
    sizeof(int());            // type-id (ill-formed)
    sizeof(int(a));           // expression
    sizeof(int(unsigned(a))); // type-id (ill-formed)
 
    (int()) + 1;            // type-id (ill-formed)
    (int(a)) + 1;           // expression
    (int(unsigned(a))) + 1; // type-id (ill-formed)
}

然而,如果歧义类型标识符中最外层的抽象声明符具有尾随返回类型,则只有当它以 auto 开头时,歧义才会被解决为类型标识符。

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    sizeof(B()->C[1]);    // OK, sizeof(expression)
    sizeof(auto()->C[1]); // error: sizeof of a function returning an array
}
(C++11 起)

[编辑] 注意

功能测试宏 标准 特性
__cpp_auto_cast 202110L (C++23) auto(x)auto{x}

[编辑] 示例

#include <cassert>
#include <iostream>
 
double f = 3.14;
unsigned int n1 = (unsigned int)f; // C-style cast
unsigned int n2 = unsigned(f);     // function-style cast
 
class C1;
class C2;
C2* foo(C1* p)
{
    return (C2*)p; // casts incomplete type to incomplete type
}
 
void cpp23_decay_copy_demo()
{
    auto inc_print = [](int& x, const int& y)
    {
        ++x;
        std::cout << "x:" << x << ", y:" << y << '\n';
    };
 
    int p{1};
    inc_print(p, p); // prints x:2 y:2, because param y here is an alias of p
    int q{1};
    inc_print(q, auto{q}); // prints x:2 y:1, auto{q} (C++23) casts to prvalue,
                           // so the param y is a copy of q (not an alias of q)
}
 
// In this example, C-style cast is interpreted as static_cast
// even though it would work as reinterpret_cast
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
 
int main()
{
    D* d = nullptr;
//  A* a = (A*)d;                   // compile-time error
    A* a = reinterpret_cast<A*>(d); // this compiles
    assert(a == nullptr);
 
    cpp23_decay_copy_demo();
}

输出

x:2 y:2
x:2 y:1

[编辑] 缺陷报告

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

缺陷报告 应用于 发布时的行为 正确的行为
CWG 1223
(P2915R0)
C++11 添加尾随返回类型引入了更多歧义 解决了它们
CWG 1893 C++11 函数风格类型转换未考虑包扩展 考虑了它们
CWG 2351 C++11 void{} 是格式错误的 使其格式正确
CWG 2620 C++98 歧义函数的解析
参数可能被误解
改进了措辞
CWG 2828 C++98 如果 static_cast 后跟 const_cast 有多种解释,则 C 风格类型转换格式错误,
无论这些转换是否实际使用
不管这些转换是否实际使用
只考虑那些
转换
可能被使用
CWG 2894 C++98 函数风格的类型转换可以创建引用右值 只能创建引用左值

[编辑] 参考

  • C++23 标准 (ISO/IEC 14882:2024)
  • 7.6.1.4 显式类型转换(函数风格记法)[expr.type.conv]
  • 7.6.3 显式类型转换(C 风格记法)[expr.cast]
  • C++20 标准 (ISO/IEC 14882:2020)
  • 7.6.1.4 显式类型转换(函数风格记法)[expr.type.conv]
  • 7.6.3 显式类型转换(C 风格记法)[expr.cast]
  • C++17 标准 (ISO/IEC 14882:2017)
  • 8.2.3 显式类型转换(函数风格记法)[expr.type.conv]
  • 8.4 显式类型转换(C 风格记法)[expr.cast]
  • C++14 标准 (ISO/IEC 14882:2014)
  • 5.2.3 显式类型转换(函数风格记法)[expr.type.conv]
  • 5.4 显式类型转换(C 风格记法)[expr.cast]
  • C++11 标准 (ISO/IEC 14882:2011)
  • 5.2.3 显式类型转换(函数风格记法)[expr.type.conv]
  • 5.4 显式类型转换(C 风格记法)[expr.cast]
  • C++03 标准 (ISO/IEC 14882:2003)
  • 5.2.3 显式类型转换(函数风格记法)[expr.type.conv]
  • 5.4 显式类型转换(C 风格记法)[expr.cast]
  • C++98 标准 (ISO/IEC 14882:1998)
  • 5.2.3 显式类型转换(函数风格记法)[expr.type.conv]
  • 5.4 显式类型转换(C 风格记法)[expr.cast]

[编辑] 另请参阅

const_cast 转换 添加或移除 const[编辑]
static_cast 转换 执行基本转换[编辑]
dynamic_cast 转换 执行受检多态转换[编辑]
reinterpret_cast 转换 执行通用低级转换[编辑]
标准转换 从一种类型到另一种类型的隐式转换[编辑]
C 文档 中关于 转换运算符 的内容