【问题标题】:Two-Step Compile for C++ Modules?C++ 模块的两步编译?
【发布时间】:2021-04-26 06:18:25
【问题描述】:

Clang 和 GCC(也许还有 MSVC?)目前正在为他们的模块实现使用两步编译:

  • 生成 BMI/CMI(MSVC 的 IPR,如果它仍然这样做?)以供其他人的导入使用。
  • 生成要馈送到链接器的目标文件。

生成 BMI/CMI 但不生成目标文件的模块似乎有一些可能的用途,例如仅导出用于条件编译的类型或 constexpr 变量的模块。

据我从标准中可以理解,没有什么说我必须生成/链接对象文件。所以我想知道在使用这样的模块时我是否遗漏了一些明显的东西,如果我们希望工具支持这种“作为模块构建,而不是作为对象构建”的工作流程?

【问题讨论】:

  • "仅导出用于条件编译的类型或 constexpr 变量的模块" 编译器如何知道这些定义仅“用于条件编译”?
  • 嗨@NicolBolas,它不是,这只是我最近一直在玩的一个用例......安迪。

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


【解决方案1】:

我希望模块能够为通常不会包含标题的内容提供定义。

想象一下这个模块:

export module hello;

export inline auto say_hello() -> char const* {
    return "hello world";
}

如您所见,该函数是内联的。它也在界面中。现在有了标题,就没有地方放实现了。为了使内联函数成为可能,该语言允许找到多个定义。所以每个 TU 在目标文件中输出自己的定义。

这是使用模块可以避免的重复工作。如您所见,模块接口就像任何其他 cpp 文件一样是 TU。当您导出内联函数时,是的,其他 TU 可以使用该实现,但 all tu 没有必要提供实现,因为它可以放在一个地方:具有里面的内联函数。

我对 constexpr 变量也有同样的期望。它们也需要定义,因为您可以为它们提供参考或地址。以此为例:

export module foo;
import <tuple>;
export constexpr auto tup = std::tuple{1, 'a', 5.6f};
import foo;
int a = std::get<0>(tup);

std::get 函数引用元组。即使它是一个 constexpr 变量,某些上下文(尤其是没有优化的)可能需要使用 ODR 变量。

因此,在我的示例中,即使模块 foo 仅导出 constexpr 变量,我希望 cpp 文件编译为包含定义的目标文件。


也可能发生目标文件中没有任何内容的情况。我也希望它今天表现得像一个空的 TU:

// I'm empty

您可以毫无问题地将此类cpp 文件添加到项目中,并将其链接到您的可执行文件。我希望这些工具与模块的行为相同。

【讨论】:

  • 嗨@Guillaume,“他们也需要定义”,真的,因为标准要求从模块导出的东西在某处定义?同样,我没有在标准中找到这个,但我的阅读可能错过了这个!当然,如果您获取参考资料或地址,他们需要一个定义,但是如果不给他们一个定义,我会阻止人们这样做(或者至少很容易这样做),我可以看到这是“这个”的理想行为事物仅在编译时才有意义”值。安迪。
  • @Andy 在模块之前需要定义的东西也需要在模块中定义。不需要定义的东西仍然不需要模块定义。您可以 ODR 使用 constexpr 变量,并且可以获取内联函数的地址。两者都需要 C++17 及之前的定义,并且仍然需要 C++20
  • @Andy 编译器可以像以前一样在每个用户 TU 中输出一个定义(虽然这会浪费资源),或者可以在导出的 TU 中输出一个定义。我不认为标准对此有意见,因为它更有可能是实现定义的行为。
  • 据我所知,clang 可能会在每个导入的 TU 中输出一个定义,因为它们的模型基于预编译的标头,并且它们通常不携带所有权信息。
猜你喜欢
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多