【问题标题】:Can static_cast throw an exception in C++?static_cast 可以在 C++ 中抛出异常吗?
【发布时间】:2025-12-24 21:35:06
【问题描述】:

假设 static_cast 永远不会抛出异常是否安全?

对于一个 int 到 Enum 的强制转换,即使它是无效的,也不会抛出异常。我可以依靠这种行为吗?以下代码有效。

enum animal {
  CAT = 1,
  DOG = 2
};

int y = 10;
animal x = static_cast<animal>(y);

【问题讨论】:

  • 根据源和目标类型,static_cast 的结果可能是未定义的行为,其中可能包括抛出异常。
  • 最好问一下,能不能在程序处于定义好的状态时抛出异常。当然 UB 理论上可以抛出异常,但是 UB 理论上也可以格式化你的硬盘。
  • @JerryCoffin:标准 5.2.9 只说某些结果可以是未定义的,虽然随后使用此类结果可能具有未定义的行为,但演员表本身没有明确提及未定义的行为。我错过了什么吗?

标签: c++ casting


【解决方案1】:

假设static_cast 永远不会抛出异常是否安全?

没有。对于用户定义的类型,构造函数和/或转换运算符可能会抛出异常,从而导致明确定义的行为。

考虑这个程序的输出:

#include <iostream>

struct A {
  A(int) { throw 1; }
};

int main () {
  int y = 7;
  try {
    static_cast<A>(y);
  } catch(...) {
    std::cout << "caught\n";
  }
}

【讨论】:

    【解决方案2】:

    对于这种特定类型的强制转换(集成到枚举类型),可能会引发异常。

    C++ 标准 5.2.9 静态转换 [expr.static.cast] 第 7 段

    整数或枚举类型的值可以显式转换为 枚举类型。如果原始值是,则该值不变 在枚举值(7.2)的范围内。 否则, 结果枚举值未指定/未定义(C++17 起)。

    请注意,由于 C++17 这种转换实际上可能导致未定义的行为,其中可能包括抛出异常。

    换句话说,您对static_cast 从整数中获取枚举值的特殊用法在 C++17 之前都可以,并且总是可以的,前提是您通过某种方式确保整数实际上代表了一个有效的枚举值输入验证程序。

    有时输入验证过程完全不需要static_cast,如下所示:

    animal GetAnimal(int y)
    {
        switch(y)
        {
        case 1:
            return CAT;
        case 2:
            return DOG;
        default:
            // Do something about the invalid parameter, like throw an exception,
            // write to a log file, or assert() it.
        }
    }
    

    请考虑使用类似上述结构的东西,因为它不需要强制转换,并且让您有机会正确处理边界情况。

    【讨论】:

      【解决方案3】:

      static_cast 不能抛出异常,因为static_cast 不是运行时强制转换,如果不能强制转换,代码将无法编译。但是如果它编译并且强制转换是错误的 - 结果是未定义的。

      【讨论】:

      • 这不一定是真的。它可能会导致源和目标类型的某些组合的未定义行为,并且所述未定义行为可能包括引发异常
      • @In silico, ForEveR:您能否就您对某些具有未定义行为的 static_cast 的声明提供一些解释?标准 5.2.9 只说某些结果可以是未定义的,虽然随后使用此类结果可能具有未定义的行为,但没有明确提及未定义的 behaviour 来自演员表本身。我错过了什么吗?
      【解决方案4】:

      (此答案专门针对您问题中的intenum 转换。)

      对于一个 int 到 Enum 的强制转换,即使它是无效的,也不会抛出异常。我可以依靠这种行为吗?以下代码有效。

      enum animal {   CAT = 1,   DOG = 2 };
      int y = 10; 
      animal x = static_cast<animal>(y); 
      

      实际上,枚举不限于其定义中的枚举列表,这不仅仅是一些奇怪的怪癖,而是枚举的一个特意利用的特性 - 考虑一下枚举值通常如何被 ORed 在一起以将它们打包成一个值,或者当没有任何枚举适用时传递 0。

      在 C++03 中,编译器将使用多大的后备整数不受程序员显式控制,但范围保证跨越 0 和显式列出的枚举。

      因此,对于animal,10 不一定是有效的、可存储的值。即使支持值不足以存储您尝试转换为 animal 的整数值,也可以应用缩小转换 - 通常这将使用枚举支持类型可以容纳的许多最低有效位,丢弃任何额外的高位,但有关详细信息,请查看标准。

      实际上,PC 和服务器硬件上的大多数现代 C++03 编译器默认使用(32 位)int 来支持枚举,因为这有助于调用以 32 位为标准的 C 库函数。

      从不期望编译器在使用static_cast&lt;&gt; 将任何值硬塞到枚举中时抛出异常。

      【讨论】: