【问题标题】:What is monomorphisation with context to C++?什么是 C++ 上下文的单态化?
【发布时间】:2013-01-07 03:47:12
【问题描述】:

Dave Herman's recent talk in Rust 说他们从 C++ 借用了这个属性。我找不到有关该主题的任何内容。谁能解释一下单态是什么意思?

【问题讨论】:

    标签: c++ rust


    【解决方案1】:

    单态化意味着生成通用函数的特殊版本。如果我编写一个函数来提取任何对的第一个元素:

    fn first<A, B>(pair: (A, B)) -> A {
        let (a, b) = pair;
        return a;
    }
    

    然后我调用这个函数两次:

    first((1, 2));
    first(("a", "b"));
    

    编译器将生成两个版本的first(),一个专门用于整数对,一个专门用于字符串对。

    这个名字来源于编程语言术语“多态”——意思是一个函数可以处理多种类型的数据。单态是从多态代码到单态代码的转换。

    【讨论】:

    • 静态调度的别称吗?
    • @Tshepang 不是真的,这更像是 C++ 模板和 Java 泛型之间的区别。
    • 在我看来与我们在 C++ 中简单地称为(隐式)模板专业化的内容相同。不要与单态混淆,单态在通过其父接口处理子类型的意义上与多态是逻辑相反的。
    • @tshepang 静态调度是单态的一种形式,特别是临时多态的单态。 Ad hoc 多态性在 Rust 中表现为特征,在 C++ 和 Java 中表现为接口。这里的其他人都指的是参数多态的单态化,在 Rust 和 Java 中称为“泛型”,在 C++ 中称为“模板”,它与静态调度非常不同,因为参数多态与临时多态非常不同。请注意,Java 不会单态化任何东西,只有 Rust 和 C++ 会。具体来说,(续)
    • (续)Rust 和 C++ 使用“专业化”来单态化参数多态性,正如公认的答案所讨论的那样,默认情况下使用静态调度来单态化即席多态性。它们还允许您通过在 Rust 中使用 dyn 或在 C++ 中使用虚拟方法来选择动态调度。相比之下,Java 对所有内容都使用动态调度,甚至包括泛型(即参数多态)。
    【解决方案2】:

    不确定是否有人还在关注这个,但 Rust 文档确实提到了它如何通过这个过程实现无成本抽象。来自Performance of Code Using Generics

    您可能想知道是否存在运行时成本 使用泛型类型参数。好消息是 Rust 实现了 以这样一种方式使用泛型,使您的代码运行速度不会变慢 与具体类型相比,泛型类型。

    Rust 通过执行代码的单态化来实现这一点 在编译时使用泛型。单态化是一个过程 通过填充具体将通用代码转换为特定代码 编译时使用的类型。

    在这个过程中,编译器执行与我们使用的步骤相反的步骤 创建示例 10-5 中的泛型函数:编译器查看 所有调用通用代码并生成代码的地方 调用通用代码的具体类型。

    让我们通过一个使用标准的示例来看看它是如何工作的 库的 Option 枚举:

    let integer = Some(5);
    let float = Some(5.0);
    

    当 Rust 编译这段代码时,它会执行单态化。中 在该过程中,编译器读取已在 Option实例和标识两种Option:一种是i32 另一个是f64。因此,它扩展了 选项转换为 Option_i32 和 Option_f64,从而替换 泛型定义与具体定义。

    代码的单态版本如下所示。这 通用选项被替换为由创建的特定定义 编译器:

    // Filename: src/main.rs
    
    enum Option_i32 {
        Some(i32),
        None,
    }
    
    enum Option_f64 {
        Some(f64),
        None,
    }
    
    fn main() {
        let integer = Option_i32::Some(5);
        let float = Option_f64::Some(5.0);
    }
    

    因为 Rust 将泛型代码编译成指定类型的代码 在每种情况下,我们都无需为使用泛型支付运行时成本。当。。。的时候 代码运行,它的执行就像我们复制每个代码一样 手动定义。单态化的过程使得 Rust 的 泛型在运行时非常高效。

    【讨论】:

    • 欢迎来到 Stack Overflow 并感谢您的回答。在提供主要由链接组成的回答时,简要总结链接文章的内容通常会有所帮助。这样,如果链接断开,您的帖子仍然有用。这样,您的回答也更有可能获得支持。
    • “无成本抽象”是非常值得怀疑的表征;较大的程序二进制大小和编译所需的时间是有代价的。
    • @user7610 零成本抽象并不是一个新概念,Bjarne 对其进行了很好的定义:“你不使用的东西,你不需要付费。更进一步:你使用的东西,你可以手写代码再好不过了。” Option 就是一个很好的例子。它在二进制文件中完全消失,它是有效的 T 或 null。当然,您可以提出一个愚蠢的论点,即仍然存在成本。毕竟,您必须实际输入这些字母。但是唯一免费的程序是你不编写的程序。
    • 正是“无成本抽象”的措辞促使我写了前面的评论。没有什么是完全没有成本的,我们能做的最好的事情就是在将成本转移到伤害较小或可行的地方进行权衡。我知道 Bjarne 的零成本抽象概念,正如您所说的那样精确定义。
    【解决方案3】:

    对此不确定;你能链接到谈话吗?这可能是一句随意的话。

    Herman 可能创造了一个类似模板专业化的术语,它从模板生成相互不相关(非多态或“单态”)的类型/对象,这是一个多态结构。

    【讨论】:

      【解决方案4】:

      Rust Book 中有一个很好的单态解释

      单态化是通过填充编译时使用的具体类型将通用代码转换为特定代码的过程。

      从书中的例子中,如果你用Some定义了变量:

      let integer = Some(5);
      let float = Some(5.0);
      

      当 Rust 编译这段代码时,它会执行单态化。在那期间 过程中,编译器读取Option&lt;T&gt;中已经使用的值 实例并识别两种Option&lt;T&gt;:一种是i32,另一种是i32f64。因此,它将Option&lt;T&gt; 的通用定义扩展为 Option_i32Option_f64,从而将通用定义替换为 具体的。

      代码的单态版本如下所示。通用的 Option&lt;T&gt; 替换为编译器创建的具体定义:

      文件名:src/main.rs

      enum Option_i32 {
          Some(i32),
          None,
      }
      
      enum Option_f64 {
          Some(f64),
          None,
      }
      
      fn main() {
          let integer = Option_i32::Some(5);
          let float = Option_f64::Some(5.0);
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-19
        • 1970-01-01
        • 2012-08-27
        • 2011-05-11
        • 2013-05-29
        • 2010-11-18
        • 2013-03-04
        相关资源
        最近更新 更多