命名空间
变体
操作

显式类型转换

来自 cppreference.cn
< cpp‎ | language
 
 
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 起)

[编辑] 注解

特性测试宏 Std 特性
__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++ 标准。

DR 应用于 已发布行为 正确行为
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 显式类型转换(强制类型转换表示法) [expr.cast]
  • C++20 标准 (ISO/IEC 14882:2020)
  • 7.6.1.4 显式类型转换(函数式表示法) [expr.type.conv]
  • 7.6.3 显式类型转换(强制类型转换表示法) [expr.cast]
  • C++17 标准 (ISO/IEC 14882:2017)
  • 8.2.3 显式类型转换(函数式表示法) [expr.type.conv]
  • 8.4 显式类型转换(强制类型转换表示法) [expr.cast]
  • C++14 标准 (ISO/IEC 14882:2014)
  • 5.2.3 显式类型转换(函数式表示法) [expr.type.conv]
  • 5.4 显式类型转换(强制类型转换表示法) [expr.cast]
  • C++11 标准 (ISO/IEC 14882:2011)
  • 5.2.3 显式类型转换(函数式表示法) [expr.type.conv]
  • 5.4 显式类型转换(强制类型转换表示法) [expr.cast]
  • C++03 标准 (ISO/IEC 14882:2003)
  • 5.2.3 显式类型转换(函数式表示法) [expr.type.conv]
  • 5.4 显式类型转换(强制类型转换表示法) [expr.cast]
  • C++98 标准 (ISO/IEC 14882:1998)
  • 5.2.3 显式类型转换(函数式表示法) [expr.type.conv]
  • 5.4 显式类型转换(强制类型转换表示法) [expr.cast]

[编辑] 参见

const_cast 转换 添加或移除 const[编辑]
static_cast 转换 执行基本转换[编辑]
dynamic_cast 转换 执行检查的运行时多态转换[编辑]
reinterpret_cast 转换 执行通用底层转换[编辑]
标准转换 从一种类型到另一种类型的隐式转换[编辑]
C 文档 关于 强制类型转换运算符