c++ - 模块接口(interface)中的内联含义

标签 c++ language-lawyer c++20 c++-modules

考虑头文件:

class T
{
private:
  int const ID;

public:
  explicit T(int const ID_) noexcept : ID(ID_) {}

  int GetID() const noexcept { return ID; }
};

或者,或者:
class T
{
private:
  int const ID;

public:
  explicit T(int const ID_) noexcept;

  int GetID() const noexcept;
};

inline T::T(int const ID_) noexcept : ID(ID_) {}

inline int T::GetID() const noexcept { return ID; }

在预模块世界中,这些 header 可能以文本形式包含在多个 TU 中,而不会违反 ODR。此外,由于涉及的成员函数相对较小,编译器可能会“内联”(在使用时避免函数调用)这些函数,甚至优化掉一些 T 的实例。共。

在最近的 report在 C++20 完成的 session 上,我可以阅读以下声明:

We clarified the meaning of inline in module interfaces: the intent is that bodies of functions that are not explicitly declared inline are not part of the ABI of a module, even if those function bodies appear in the module interface. In order to give module authors more control over their ABI, member functions defined in class bodies in module interfaces are no longer implicitly inline.



我不确定我没有弄错。这是否意味着,在模块世界中,为了让编译器能够优化函数调用,我们必须将它们注释为 inline即使它们是在类里面定义的?

如果是这样,下面的模块接口(interface)是否等同于上面的标题?
export module M;

export
class T
{
private:
  int const ID;

public:
  inline explicit T(int const ID_) noexcept : ID(ID_) {}

  inline int GetID() const noexcept { return ID; }
};

即使我还没有支持模块的编译器,我还是想开始使用 inline在适当的时候像这样,以尽量减少 future 的重构。

最佳答案

Does that mean that, in a modules world, for the compiler to be able to optimize away function calls we have to annotate them as inline even if they are defined in-class?



在某种程度上。

内联是一种“好像”的优化,如果编译器足够聪明,内联甚至可以在翻译单元之间发生。

话虽如此,在单个翻译单元中工作时,内联是最容易的。因此,为了促进简单的内联,inline -declared 函数必须在使用它的任何翻译单元中提供其定义。这并不意味着编译器一定会内联它(或者肯定不会内联任何非 inline -qualified 函数),但它确实使内联过程变得更容易,因为内联发生在 TU 内而不是它们之间。

类中定义的类成员定义,在模块前的世界中,声明为 inline含蓄地。为什么?因为定义在类中。在模块前的世界中,在 TU 之间共享的类定义通过文本包含共享。因此,在类中定义的成员将在这些 TU 之间共享的 header 中定义。因此,如果多个 TU 使用同一个类,则这些多个 TU 是通过在 header 中包含类定义及其成员的定义来实现的。

也就是说,无论如何你都包含了定义,所以为什么不让它 inline ?

当然,这意味着函数的定义现在是类文本的一部分。如果您更改在 header 中声明的成员的定义,这将强制重新编译包含该 header 的每个文件,递归。即使类的接口(interface)本身没有改变,你仍然需要重新编译。所以隐式地制作这样的函数inline不会改变这一点,所以你也可以这样做。

为避免在预模块世界中出现这种情况,您可以简单地在 C++ 文件中定义成员,该成员不会包含在其他文件中。你失去了简单的内联,但你获得了编译时间。

但事情是这样的:这是使用文本包含作为将类(class)传递到多个地方的手段的产物。

在模块化世界中,您可能希望在类本身中定义每个成员函数,正如我们在 Java、C#、Python 等其他语言中看到的那样。这使代码局部性保持合理,并避免重新键入相同的函数签名,从而满足 DRY 的需求。

但是如果所有成员都在类定义中定义,那么在旧规则下,所有这些成员将是 inline .并且为了让模块允许函数为 inline ,二进制模块工件必须包含这些函数的定义。这意味着任何时候你在这样的函数定义中改变了一行代码,就必须递归地构建模块,以及依赖它的每个模块。

删除隐式- inline in modules 为用户提供了与文本包含时代相同的权力,而无需将定义移出类。您可以选择哪些函数定义是模块的一部分,哪些不是。

关于c++ - 模块接口(interface)中的内联含义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60280105/

相关文章:

c++ - =声明与定义中的默认值

c++ - 使用先前的函数参数声明新的函数参数是否合法?

c++ - 如何指定指向参数数量未知的 C 函数的 C++ 指针?

javascript - ECMAScript 规范中的 `new Object` 与 `Object`

c++ - 值初始化 std::array 成员?

c++ - 排序后访问 vector 的第 n 个元素(值)

c++ - 模拟 C++ 标准库

c++ - 在 C++20 之前将 malloc 用于 int 未定义行为

C++ 警告 : pragma once in main file

c++ - span 是否传播 const?