命名空间
变体
操作

显式类型转换

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

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

内容

[编辑] 语法

( 目标类型 ) 表达式 (1)
目标类型 ( 表达式列表 (可选) ) (2)
目标类型 { 表达式列表 (可选) } (3) (自 C++11 起)
模板名称 ( 表达式列表 (可选) ) (4) (自 C++17 起)
模板名称 { 表达式列表 (可选) } (5) (自 C++17 起)
auto ( 表达式 ) (6) (自 C++23 起)
auto { 表达式 } (7) (自 C++23 起)

返回类型为 目标类型 的值。

[编辑] 解释

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_castreinterpret_cast 是未定义的。
2) 函数式强制转换表达式 由一个简单类型说明符或 typedef 说明符组成(换句话说,一个单字类型名称,例如 unsigned int(表达式)int*(表达式) 无效),后跟括号内的逗号分隔表达式列表。
  • 如果括号内只有一个表达式,则此强制转换表达式与相应的 C 样式强制转换表达式完全等效。
  • 如果括号内有多个表达式 花括号初始化列表(自 C++11 起),则 目标类型 必须是具有适当地声明的 构造函数 的类。此表达式是一个 目标类型 类型的右值 指定一个临时对象(直到 C++17)其结果对象是(自 C++17 起) 直接初始化,其初始化器为 ( 表达式列表 )
  • 如果括号内没有表达式:如果 目标类型 指示一个非数组的完整对象类型,则此表达式是一个 目标类型 类型的右值 指定一个临时对象(直到 C++17)其结果对象是 (可能添加了 cv 限定符)(自 C++17 起) 该类型。如果 目标类型 是一个对象类型,则该对象将进行 值初始化。如果 目标类型 是 (可能 cv 限定) 的 void,则该表达式是一个 void 类型的右值 没有结果对象(自 C++17 起)
3) 由单字类型名称后跟花括号包围的初始化器列表组成的表达式是一个指定类型的右值 指定一个临时对象(直到 C++17)其结果对象是(自 C++17 起) 直接列表初始化,其初始化器为 { 表达式列表 }。如果 目标类型 是 (可能 cv 限定) 的 void,则该表达式是一个 void 类型的右值 没有结果对象(自 C++17 起) 这是唯一可以创建 数组右值 的强制转换表达式。(直到 C++20)
4,5)(2,3) 相同,区别在于先进行 类模板参数推断
6,7) auto 说明符被 推断 出的 表达式 类型替换。结果始终是对象类型类型的右值。

与所有强制转换表达式一样,结果为

  • 左值,如果 目标类型 是左值引用类型 或指向函数类型的右值引用(自 C++11 起)
  • 右值,如果 目标类型 是指向对象类型的右值引用;
(自 C++11 起)
  • 其他情况下为右值。

[编辑] 歧义解决

[编辑] 歧义声明语句

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

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 us 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++ 标准。

DR 应用于 已发布的行为 正确行为
CWG 1223
(P2915R0)
C++11 添加尾随返回类型引入了更多歧义 解决它们
CWG 2620 C++98 对歧义函数参数的解决可能被误解 改进了措辞
CWG 2828 C++98 如果有多种解释
static_cast 后跟 const_cast 存在,则 C 样式强制转换将是格式错误的,
无论这些转换是否实际使用
仅考虑
转换
可能被使用

[编辑] 参考资料

  • 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 文档 for 强制转换运算符