【问题标题】:hpp/cpp split of C++20 module implementation partitionsC++20 模块实现分区的 hpp/cpp 拆分
【发布时间】:2021-11-25 12:25:59
【问题描述】:

我有一个大类Foo,我想把它变成一个模块分区。这个类只在内部使用,所以我不需要导出任何符号。一个模块实现分区应该这样做:

export module parent;
import :foo;

module parent:foo;
class Foo { ... }; urg! I need to add the full implementation here, no more hpp/cpp split

我可以通过一个模块接口分区parent:foo 来解决这个问题,该分区将包含我的头文件但不会导出任何内容。不过我不是这个想法的忠实粉丝。

这里推荐的方法是什么?当什么都没有被导出时,“老式”的 hpp/cpp 是否应该与模块分开?

【问题讨论】:

    标签: c++ c++20 c++-modules


    【解决方案1】:

    您仍然可以以几乎相同的方式使用单独的源文件:

    module parent:foo;
    class Foo {
      void f();
    };
    
    module parent;
    import :foo;
    
    void Foo::f() {}
    

    后一个实现单元,只是在别处声明的函数的定义,不需要在任何地方导入,只需像往常一样链接。相同的方法适用于导出的类,只是它们当然必须在接口单元中声明。

    您是否想要将来使用这种结构是一个单独的问题:即使没有重复隔离,模块也可以使您的构建快速足够,并且您的实现可能作为功能,拒绝内联未在模块单元中声明 inline 的函数(即使在类中定义)以提供 ABI 稳定性。

    【讨论】:

    • 不确定我是否关注。 “不需要在任何地方进口”?您仍然需要父模块中的import :foo,不是吗?在您的示例中,您在父模块中有定义。你的意思是你也可以有多个实现分区,一个包含声明(~hpp),另一个包含定义(~cpp)?
    • @Touloudou:在模块的任何部分使用实现分区都需要import :foo;,但这仅导入这两个翻译单元中的第一个。后者不是任何形式意义上的分区,即使我们认为它是parent:foo的实现。
    • 如果我只能导入一个实现分区,那我该如何解决这个问题呢?您提供的示例具有父模块中的定义。在大型应用程序中,我不想将所有孩子的实现都放在父模块中。
    • @Touloudou:我们沟通不畅。您可以随意import :foo; import :baz;。您还可以拥有任意数量的带有纯 module parent; 的翻译单元(从这个意义上说,该名称具有误导性)。这些实现单元不是分区的“一部分”,但它们不需要。
    • @alexpanter:就实现选择这种行为而言,模块恢复了inline 的一些原始含义,因为它们实际上不会将模块单元中的任何非内联函数内联到任何在不同的翻译单元中定义的函数。除了提供 ABI 稳定性之外,这还可以通过允许内部链接函数永远不会为它们生成任何代码来改进优化。 (ODR 问题实际上并不存在于模块中,因为附加到命名模块的任何函数总共只有一个定义。)
    【解决方案2】:

    如果你想保持你的文件结构,没有什么能阻止你转换

    foo.hpp

    class Foo { ... };
    

    foo.cpp

    #include "foo.hpp"
    
    Foo::Foo() ...
    

    进入

    foo.hpp

    module parent:foo;
    class Foo { ... };
    #include "foo.cpp"
    

    foo.cpp

    Foo::Foo() ...
    

    【讨论】:

    • 这在技术上是一个可行的解决方案,但是如果foo.hpp 的处理依赖于预处理器配置会带来问题:"在翻译单元中定义的预处理宏不会影响头文件的处理. 这在某些情况下可能不方便(某些头文件使用预处理宏作为一种配置形式),在这种情况下需要使用全局模块片段。” (cppreference)
    【解决方案3】:

    使用模块时不需要任何头文件。这是将模块引入语言的核心思想 - 逐渐终止对标头包含的需求。您的示例代码看起来非常好,特别是如果您希望 Foo 类仅在模块内可见:

    // parent.cpp
    export module parent;
    import :foo;
    
    // Here we can use all functionality provided by
    // the foo partition without worrying that it will
    // be leaked outside this module.
    
    // parent-foo.cpp
    
    // All code in here will only be visible inside the
    // parent.cpp file.
    
    module parent:foo;
    class Foo { /* ... */ };
    

    或者,如果您愿意,您也可以将所有内容放在一个文件中。编译器不会在意,尽管该文件可能会变大:

    // parent.cpp
    export module parent;
    
    class Foo { /* ... */ } // not visible outside this module!
    
    export
    {
        // Everything placed in here will be visible to other
        // files that import this module.
    }
    
    

    【讨论】:

      猜你喜欢
      • 2016-11-15
      • 2010-12-15
      • 2016-06-29
      • 1970-01-01
      • 2021-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多