【问题标题】:What exactly is or was the purpose of C++ function-style casts?C++ 函数式强制转换的目的到底是什么?
【发布时间】:2010-12-17 21:34:41
【问题描述】:

我说的是“类型(值)”风格的演员表。我读过的书很快就忽略了它们,只说它们在语义上等同于 C 风格的强制转换,“(类型)值”,并且应该避免使用它们。如果它们的含义与旧式演员表的含义相同,那么为什么将它们添加到语言中?此外,因为声明可以包含多余的括号,所以此代码:“T x(T(y));”不会像打算使用函数式强制转换的人所期望的那样做;它声明了一个名为 x 的函数,它接受一个 T 并返回一个 T,而不是通过将 y 转换为 T 来构造一个名为 x 的 T 变量。

它们是语言设计上的错误吗?

【问题讨论】:

    标签: c++ casting language-design


    【解决方案1】:

    函数样式转换为原始类型和用户定义类型带来了一致性。这在定义模板时非常有用。例如,举这个非常愚蠢的例子:

    template<typename T, typename U>
    T silly_cast(U const &u) {
      return T(u);
    }
    

    我的silly_cast 函数适用于原始类型,因为它是函数样式的转换。它也适用于用户定义的类型,只要类 T 有一个接受 U 或 U const & 的单参数构造函数。

    template<typename T, typename U>
    T silly_cast(U const &u) {
        return T(u);
    }
    
    class Foo {};
    class Bar {
    public:
        Bar(Foo const&) {};
    };
    
    int main() {
        long lg = 1L;
        Foo f;
        int v = silly_cast<int>(lg);
        Bar b = silly_cast<Bar>(f);
    }
    

    【讨论】:

    • 这似乎与模板中的有用相反。在泛型编程中,我会格外小心避免 C 风格和函数风格的强制转换,因为例如你的 silly_cast 可以做任何 C 风格的演员可以做的事情,目前尚不清楚这是否是故意的。
    • 在您的示例中,Foo 可以隐式转换为 Bar。由于long 也可以隐式转换为int,因此根本不需要转换。另一方面,如果您显式声明构造函数,则需要进行强制转换,但static_cast 也可以使用,而且语法更简洁。
    • 您的示例似乎没有显示T(x) 的原因。您可以将您的模板写入return (T)u;,它完全等效。
    • 就像我说的,这是一个愚蠢的例子。重点是演示统一符号:-)
    【解决方案2】:

    它们的目的是让您可以将多个参数传递给类的构造函数:

    T(a1, a2); // call 2-argument constructor
    (T)(a1, a2); // would only pass a2.
    

    (T) expr 样式转换没有机制能够传递多个参数,因此需要一种新的转换形式。将(T) expr 定义为T(expr) 的简并情况是很自然的。

    与这里的某些人所说的相反,(T) expr 的工作方式与 T(expr) 完全一样,因此它也适用于类类型。

    【讨论】:

    • 我们如何定义这些多个参数会发生什么?在T(a1, a2); // call 2-argument constructor 的情况下...
    【解决方案3】:

    在括号已经被大量(过度)使用的语言中解析函数样式转换会稍微容易一些。

    他们是不是搞错了?可能——但仅限于它们无法完全取代 C 风格的强制转换,因此提供了一种类似 Perl 的“有不止一种方法可以做到”的强制转换机制。具有明确而独特的现代演员阵容:

    dynamic_cast<type>(value);
    reinterpret_cast<type>(value);
    static_cast<type>(value);
    const_cast<type>(value);
    

    没有理由再使用 C 风格的强制转换,也没有理由使用函数风格的强制转换了。

    【讨论】:

    • 我实际上喜欢 C++ 风格转换的冗长。它有助于阻止过度施法,并使施法像拇指疼痛一样突出。
    【解决方案4】:

    不应使用 C 风格的强制转换。

    应该使用函数样式转换,尤其是当目标类型是类名(或类模板特化)时。它们符合带有一个参数的案例中明显的构造函数调用模式。

    MyClass( expr );   // Creates temporary, initialized like MyClass obj( expr );
    MyClass( e1, e2 ); // Similar, and no other way to write this.
    int( expr );       // Function-style cast.
    

    【讨论】:

    • 我假设您的意思是内置算术类型。对于指针和引用,使用命名转换确实是一个更好的主意。一个 c 风格的演员表,无论是表达为 (T)x 还是 T(x),都可以做你意想不到的事情,而且很难看出它到底做了什么
    【解决方案5】:

    AFAIK 函数样式转换是对类的临时创建的常用语法的原生类型的扩展:只要您可以使用语法 ClassName(parameters) 在表达式中创建临时对象,就没有理由应该这样做t 使用本机类型执行此操作。 编辑 请注意,正如@steve 所说,这在模板中非常有用

    请注意,C++ 中的单参数构造函数通常以某种方式“感觉”为转换工具,例如,参见语法 (conversion constructor),它允许您使用后跟等号来初始化新对象通过一个值

    ClassName MyObject = 3;
    

    这实际上意味着

    ClassName MyObject(3);
    

    并且(大概)调用 ClassName 的 one-int-parameter 构造函数。

    顺便说一句,here 是一个关于函数样式转换的好页面。

    【讨论】:

      【解决方案6】:

      这是什么书??? C 风格的演员阵容通常被认为是一个坏主意。大多数现代 C++ 代码看起来像这样(当需要强制转换时)

      x = sometype( y );
      

      而不是

      x = (sometype) y;
      

      除此之外,前一种语法看起来更像是一个构造函数调用,在大多数情况下可能是这样,尽管这两种形式在语义上实际上是相同的。

      【讨论】:

      • 嗯,这只是符号。即个人喜好。我更喜欢前者,但后者没有问题。真正的分界线在 c 风格的演员表(不管符号)和命名演员表之间。命名演员更安全,因为每个人只做一项工作,并通过名字表明意图,他们更容易被识别
      • @Alf 是的,但符号很重要。但实际上,几乎不需要在编写良好的 C++ 代码中使用任何一种转换,因为编译器会为您完成。我自己的代码中唯一的转换(除了一些在 I/O 中获得正确格式的琐碎转换)是 dynamic_casts。
      • 当我读过的“大多数现代 C++ 代码”几乎完全使用 cpp_cast&lt;type&gt;(operand) 时,它显示了人们的体验有何不同。我读的代码太少还是只是更好的代码? (fwiw,我阅读的绝大多数代码都是我自己的......)无论哪种方式,我都会坚持明确的意图信号 C++ 强制转换,所以我不必猜测我的意思。如果其他人也这样做就好了!
      • 实际上,有充分的理由更喜欢static_cast&lt;type&gt;(operand)。如果您想在代码库中搜索强制转换,请尝试寻找成对的括号 :) 虽然有时这可能是正确的做法,但强制转换从来都不是一件好事,而且应该有丑陋的语法。
      • 作为参考,C++ 核心指南有一个与此相关的部分:ES.49: If you must use a cast, use a named cast。请注意,至少在我链接此内容时,该指南还指出“在没有信息丢失的类型之间进行转换时(例如,从 float 到 double 或从 int32 到 int64),可以使用大括号初始化”。因此,似乎不鼓励 sometype(y) 而鼓励 sometype{y}
      猜你喜欢
      • 1970-01-01
      • 2011-05-09
      • 2011-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多