Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

16 Nov 2020

C++20 模块规范阅读笔记【二】

C++ Modules规范阅读笔记。

10.3 Import declaration

模块导入声明的结构如下:

 module-import-declaration:
  import-keyword module-name attribute-specifier-seqopt ;
  import-keyword module-partition attribute-specifier-seqopt ;
  import-keyword header-name attribute-specifier-seqopt ; 

导入声明只能出现在全局命名空间。在一个模块单元内,所有导入声明,以及带有导入声明的导出声明,必须在其他声明之前。

导入声明导入一个翻译单元的合集。

使用导入声明导入一个模块M,那么M所有的接口单元都被导入。

对于目标是模块片区的导入声明,只能出现在模块定义之后,而且只能作为该模块定义的片区。

如果导入声明的目标是一个合适的头文件单元(synthesized header unit),也就是经历了翻译阶段1到7的翻译单元,并且此头文件单元中不带模块声明。在此头文件单元中,所有的符号都被导出,并且添加到全局空间中。

如果有两个目标是importable header的导入声明:

  • 如果头文件名字不同,那么这两个声明导入的是不同的头文件单元
  • 否则,如果它们出现在同一个翻译单一,那么它们导入同一个头文件单元
  • 否则,属于未定义行为

导入声明中出现的头文件名字,在预处理的时候也会被预处理器识别。

即便是在所有声明被导出的情况下,头文件单元中是具有内部链接性质的名字在允许的。头文件单元不能包含那么具有外部链接性质的非inline的函数或者变量。

当导入声明导入了一个翻译但与T,它也同时导入了所有T导入后导出的翻译单元。

模块实现单元不能被导出。

下面是例子。

翻译单元#1:

module M:Part;

翻译单元#2:

export module M;
export import :Part;    // error: exported partition :Part is an implementation unit

某个模块的的实现单元不能导入模块自身:

module M;
import M;               // error: cannot import M in its own unit

要避免循环引用

M1的接口单元:

export module M1;
import M2;

M2的接口单元:

export module M2;
import M3;

M3的接口单元:

export module M3;
import M1;              // error: cyclic interface dependency M3→M1→M2→M3

10.4 Global module fragment

全局模块片段结构定义如下:

 global-module-fragment:
  module-keyword ; declaration-seqopt 

一个模块单元可以有一部分片段属于全局模块。此分段内的声明属于全部模块。

然后文中列举了一大堆规则,说明什么是decl-reachable。也说明了什么是未定义行为。

在全局模块分段中的声明必须是对于翻译单元内部的任何声明都是decl-reachable的。否则就会被丢弃。丢弃的声明不会出现在模块单元之外的名字查找中,也不会出现在模块之外的模板实例化中。

例子1:

const int size = 2;
int ary1[size];                 // unspecified whether size is decl-reachable from ary1
constexpr int identity(int x) { return x; }
int ary2[identity(2)];          // unspecified whether identity is decl-reachable from ary2

template<typename> struct S;
template<typename, int> struct S2;
constexpr int g(int);

template<typename T, int N>
S<S2<T, g(N)>> f();             // S, S2, g, and ​::​ are decl-reachable from f

template<int N>
void h() noexcept(g(N) == N);   // g and ​::​ are decl-reachable from h

例子2:

foo.h:

namespace N {
  struct X {};
  int d();
  int e();
  inline int f(X, int = d()) { return e(); }
  int g(X);
  int h(X);
}

模块M的接口:

module;
#include "foo.h"

export module M;
template<typename T> int use_f() {
  N::X x;                       // N​::​X, N, and ​::​ are decl-reachable from use_­f
  return f(x, 123);             // N​::​f is decl-reachable from use_­f,
                                // N​::​e is indirectly decl-reachable from use_­f
                                //   because it is decl-reachable from N​::​f, and
                                // N​::​d is decl-reachable from use_­f
                                //   because it is decl-reachable from N​::​f
                                //   even though it is not used in this call
}
template<typename T> int use_g() {
  N::X x;                       // N​::​X, N, and ​::​ are decl-reachable from use_­g
  return g((T(), x));           // N​::​g is not decl-reachable from use_­g
}
template<typename T> int use_h() {
  N::X x;                       // N​::​X, N, and ​::​ are decl-reachable from use_­h
  return h((T(), x));           // N​::​h is not decl-reachable from use_­h, but
                                // N​::​h is decl-reachable from use_­h<int>
}
int k = use_h<int>();
  // use_­h<int> is decl-reachable from k, so
  // N​::​h is decl-reachable from k

模块M的实现:

module M;
int a = use_f<int>();           // OK
int b = use_g<int>();           // error: no viable function for call to g;
                                // g is not decl-reachable from purview of
                                // module M's interface, so is discarded
int c = use_h<int>();           // OK

(本篇完)

comments powered by Disqus