【问题标题】:Is it OK to use C-style cast for built-in types?可以对内置类型使用 C 样式转换吗?
【发布时间】:2010-10-06 09:26:43
【问题描述】:

在这里阅读了很多关于 C++ 中 C 风格转换的答案后,我还有一个小问题。我可以对像 long x=(long)y; 这样的内置类型使用 C 风格的转换吗?或者它仍然被认为是坏的和危险的?

【问题讨论】:

    标签: c++ casting


    【解决方案1】:

    我可以对像 long x=(long)y; 这样的内置类型使用 C 风格的转换吗?或者它仍然被认为是坏的和危险的?

    永远不要使用它们。反对使用它们的原因也适用于此。基本上,一旦你使用它们,所有的赌注都没有了,因为编译器将不再帮助你。虽然这对指针来说比其他类型更危险,但它可能仍然很危险,并且在出现错误的情况下提供较差的编译器诊断,而新样式转换提供更丰富的错误消息,因为它们的使用受到更多限制:Meyers引用了抛弃constness 的示例:使用除const_cast 之外的任何强制类型都不会编译,因此这里发生了什么 很清楚。

    此外,无论类型如何,其他一些缺点都适用,即语法考虑: C 风格的强制转换非常不显眼。这不好:C++ 强制转换在代码中很明显,并指向潜在的危险代码。它们也可以在 IDE 和文本编辑器中轻松搜索。尝试在大型代码中搜索 C 风格的转换,您会发现这有多难。

    另一方面,与 C++ 类型转换相比,C 风格的转换没有任何优势,因此甚至不需要考虑权衡。

    更一般地说,Scott Meyers 建议在 Effective C++(第 27 条)中“最小化类型转换”,因为“类型转换颠覆了类型系统。”

    【讨论】:

    • 关于减少演员阵容是正确的。但是你能认真地指出任何不做 float y = 1.0; 的理由吗?双 x = 双(y)?因为是问题所在。与真正危险的演员相比,这种演员并不危险,可能不会分散注意力(如果你做得对,你几乎不需要)..
    • 好吧,在那种非常特殊的情况下,甚至不需要强制转换,因为 double 是比 float 更大的类型。
    • 我不相信任何这些原因(除了可以说“应该是丑陋的,所以他们脱颖而出”,我一般不同意)适用于演员表在数字类型中;看我的回答。
    • @Zack 他们没有。但是有一个普遍适用的简单规则比只有一个奇怪的例外要好得多。特别是因为始终应用该指南基本上没有缺点。
    • “casts subvert the type system”听起来很奇怪。我不会说像int convertDoubleToInt(double) 这样的函数会颠覆类型系统。但这几乎就是static_cast<int>(myDouble) 所做的。颠覆类型系统的是const_castreinterpret_cast,但不是其他两种类型。
    【解决方案2】:

    我不会,原因如下:

    • 演员表很难看,应该很难看并且在您的代码中突出,并且可以使用 grep 和类似工具找到。
    • “始终使用 C++ 类型转换”是一个简单的规则,比“对用户定义的类型使用 C++ 类型转换,但可以对内置类型使用 C 样式类型转换”更容易记住和遵循。 "
    • C++ 样式转换为其他开发人员提供了有关为什么需要转换的更多信息。
    • C 风格强制转换可能会让您进行不希望的转换 - 如果您有一个接受 (int*) 的接口,并且您使用 c 风格强制转换向它传递一个 const int*,并且该接口更改需要很长时间*,您使用 c 样式转换的代码将继续工作,即使它不是您想要的。

    【讨论】:

    • 我不认为这些原因(除了可以说第一个)适用于数字类型之间的强制类型转换;看我的回答。
    【解决方案3】:

    如果你将 from 转换为数字类型,to 另一种数字类型,那么我认为 C 样式转换 preferable 到 @987654321 @。每个*_cast 运算符都有一个特定的理由不在数字类型上使用它:

    • reinterpret_cast,当应用于数字类型时,会进行正常的数字转换,而不是重新解释位,即编写 reinterpret_cast<uint64_t>(3.14159) 不会产生与浮点常量具有相同位表示的整数。这与直觉相反。
    • const_cast 在数字 value 上从来不需要,如果应用于数字 variable(与指针或对数字的引用相反),则表明该变量不正确。
    • dynamic_cast just plain 对数字没有意义,因为数字从来没有动态类型。
    • static_cast 通常用于类类型。因此,将static_cast 应用于数字看起来很奇怪;您的读者会摸不着头脑,想知道static_cast 是否有一些他们不知道的地方,这使得它在应用于数字时与 C 风格的演员表不同。 (事实上​​,它们是相同的,但我必须多次阅读相关的 C++ 规范部分才能确定它们是相同的,而且我仍然有“这里肯定发生了一些奇怪的事情”的反应.)

    还有一个避免它们的额外风格原因:

    • 如果您需要在数字类型之间进行转换,您可能需要在一个集群中执行其中的几个;正因为如此,C 风格转换的简洁性很重要。例如,我现在正在编写的程序有一大堆这样的事情:

      uint32_t n = ((uint32_t(buf[0]) << 24) |
                    (uint32_t(buf[1]) << 16) |
                    (uint32_t(buf[2]) <<  8) |
                    (uint32_t(buf[3])      ));
      

      在这里使用static_cast 会掩盖算术,这很重要。 (注意:只有在 sizeof(uint32_t) &lt;= sizeof(unsigned int) 时才不需要这些强制转换,这 现在是一个安全的假设,但我仍然更喜欢明确性。)(是的,我可能应该将此操作分解为 be32_to_cpu内联助手,但我仍然会以相同的方式对其进行编码。)

    【讨论】:

    • 您关于 static_cast 与 c 样式转换相同的断言是不正确的。 C 风格的转换可以根据情况解析为 reinterpret_cast、const_cast 或 static_cast。
    • @PeterSW C 风格转换与static_cast 相同用于数字类型之间的转换
    • 如果您要转换为可以保存指针的整数类型,那么使用 static_cast 可以使您免于将 reinterpret_cast 意外隐藏为 C 样式转换。如果您要转换的类型是您认为是整数类型的指针,就会发生这种情况。嗯,写到我觉得我有点抓不住了……在我们开始讨论之前,我想在转换为数字类型时可能会发现更多潜在的程序员错误。现在我想我刚才描述的案例是唯一的。
    • @PeterSW 我的建议仅适用于 源和目标在上下文中明确为数字类型的情况。如果可能涉及到指针,我同意新型强制转换运算符更安全。
    • "static_cast 通常用于类类型。因此,将static_cast 应用于数字看起来很奇怪;" 这听起来像是个人期望,并没有似乎基于任何事实。
    【解决方案4】:

    在我看来,在使用标准库函数和 STL 时对内置类型进行 C 样式转换是可以的,并且可以使代码更易于阅读。

    在我工作的公司中,我们编译时会发出最多(4 级)警告,所以我们会收到关于每个小类型转换等的警告...... 所以我在这些中使用 c 风格的转换,因为它们很小,不是那么冗长且有意义。

    for (int i = 0; i < (int)myvec.size(); i++)
    {
      // do something int-related with i
    }
    float val = (float)atof(input_string);
    

    等等……

    但是如果它的 on(例如库)代码可能会改变,那么 static_cast() 会更好,因为如果类型改变并且强制转换不再有意义,您可以确保编译器会出错。此外,如果您只使用 c 样式,则不可能在代码中搜索强制转换。 “static_cast&lt;mytype&gt;(”很容易搜索。 :)

    【讨论】:

      【解决方案5】:

      我能想到 C 风格转换的一种合法用途:

      // cast away return value to shut up pedantic compiler warnings
      (void)printf("foo\n");
      

      【讨论】:

        【解决方案6】:

        考虑到上下文,我认为这可能没问题。示例:

        /* Convert -1..1 to -32768..32767 */
        short float_to_short(float f)
        {
            return (short)(max(-32768.f, min(32767.f, f * 32767.f)));
        }
        

        【讨论】:

          【解决方案7】:

          如果你发现你需要进行 C 风格的转换(或 reinterpret_cast),那么请非常仔细地查看你的代码,它 99.99% 肯定它有问题。这两种转换几乎都不可避免地导致特定于实现(并且通常是未定义的)行为。

          【讨论】:

          • “如果你对编译器撒谎,它会惩罚你”。这些演员表要么是谎言,要么是试图修复先前的谎言。
          • 在研究代码中,我经常看到并排处理浮点数和双数,或者例如来自图像的 int 数据被强制转换为 float/double 以进行进一步处理。这些是演员表的真实用例,我认为它们超过 0.01%。
          • Windows 上线程函数的参数呢?你必须通过 void*。常见的模式是通过 reinterpret_cast(this).
          • 这样的回答让我想知道人们以前是否写过真正的代码。浮点类型之间的强制转换(即使你试图避免它们,因为它们非常慢)、有符号和无符号整数、整数以及这些天返回的任何可怕的类型 vector::size() 等都是绝对不可避免的,而且在实际、实用的代码,尤其是当您将库与您无法控制的 API 粘合在一起时。
          【解决方案8】:

          你为什么需要那个特定的演员表?任何数字类型都可以在没有强制转换的情况下转换为 long(可能会损失精度),因此强制转换不会让编译器做任何它已经不能做的事情。通过强制转换,您所做的只是消除编译器在存在潜在问题时发出警告的能力。如果您将其他一些基本类型(如指针)转换为 long,我真的很想看到 reinterpret_cast 而不是 C 类型转换,所以如果结果是我可以很容易地找到发生了什么成为一个问题。

          我不会无缘无故地赞成强制转换,我当然也不会无故赞成 C 型强制转换。我认为没有理由在大多数内置类型之间进行转换,如果有的话,我希望能够通过文本搜索轻松找到它。

          【讨论】:

          • 如果您不希望编译器产生精度损失错误,那么强制转换是必要的
          • 当然,但你最好暂时压制它们。当我们进行 64 位处理(需要更多内存空间)时,如果我们一直在进行转换,我们会在某些情况下失去精度(涉及 size_t)。
          【解决方案9】:

          我更喜欢传统 C 转换的一种情况是将字节缓冲区转换为不同的符号。许多 API 有不同的约定,并没有真正的“正确答案”,并且转换在它完成的上下文中并不危险,其中代码只需要与所使用的库的组合一样与平台无关。

          我的意思是一个具体的例子,我认为这很好:

          unsigned char foo[16];
          lib1_load_foo(key);
          lib2_use_foo((char*)key);
          

          但是对于其他任何事情,如果需要强制转换,它会产生潜在的副作用,这应该很突出,使用 C++ 风格(可以说是丑陋的)强制转换是正确的选择。如果不需要强制转换,则不要使用强制转换。

          【讨论】:

            猜你喜欢
            • 2023-03-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-19
            • 2018-06-07
            • 2016-05-07
            • 2017-07-15
            • 2019-10-22
            相关资源
            最近更新 更多