Namespaces
Variants

Translation-unit-local entities (since C++20)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

翻译单元局部(TU-local)实体被引入,旨在防止本应局部化(不在任何其他翻译单元中使用)的实体在其他翻译单元中暴露和使用。

来自 理解C++模块:第二部分 的示例说明了未约束暴露的问题:

// 无翻译单元局部约束的模块单元
export module Foo;
import <iostream>;
namespace
{
   class LolWatchThis {        // 内部链接,不可导出
       static void say_hello()
       {
           std::cout << "Hello, everyone!\n";
       }
   };
}
export LolWatchThis lolwut() { // LolWatchThis 作为返回类型被暴露
    return LolWatchThis();
}
// main.cpp
import Foo;
int main()
{
    auto evil = lolwut();        // 'evil' 的类型为 'LolWatchThis'
    decltype(evil)::say_hello(); // 'LolWatchThis' 的定义不再是内部定义
}

目录

TU局部实体

一个实体如果满足以下条件则被称为 TU局部

  1. 具有以下特征的类型、函数、变量或模板:
    1. 具有<内部链接>名称的实体,或
    2. 无链接名称且在TU局部实体的定义内通过 声明或引入的实体
  2. 在<类说明符>、函数体或初始化器外部定义的无名类型,或通过仅用于声明TU局部实体的定义类型说明符(类型说明符、类说明符或枚举说明符)引入的类型
  3. TU局部模板的特化
  4. 包含任何TU局部模板参数的模板特化
  5. 其(可能实例化的)声明属于暴露(定义见下文)的模板特化
// 具有内部链接的翻译单元局部实体
namespace { // 无名命名空间内声明的所有名称均具有内部链接
    int tul_var = 1;                          // 翻译单元局部变量
    int tul_func() { return 1; }              // 翻译单元局部函数
    struct tul_type { int mem; };             // 翻译单元局部(类)类型
}
template<typename T>
static int tul_func_temp() { return 1; }      // 翻译单元局部模板
// 翻译单元局部模板特化
template<>
static int tul_func_temp<int>() { return 3; } // 翻译单元局部特化
// 使用翻译单元局部模板参数的模板特化
template <> struct std::hash<tul_type> {      // 翻译单元局部特化
    std::size_t operator()(const tul_type& t) const { return 4u; }
};

一个值或对象是 TU-local 的,当且仅当满足以下任一条件:

  1. 它是或指向一个TU局部函数,或是与TU局部变量关联的对象,或
  2. 它是类类型或数组类型的对象,且其任意 子对象 ,或其引用类型的非静态数据成员所引用的任意对象或函数是TU局部且 可用于常量表达式 的。
static int tul_var = 1;             // TU局部变量
static int tul_func() { return 1; } // TU局部函数
int* tul_var_ptr = &tul_var;        // TU局部:指向TU局部变量的指针
int (* tul_func_ptr)() = &tul_func; // TU局部:指向TU局部函数的指针
constexpr static int tul_const = 1; // 可在常量表达式中使用的TU局部变量
int tul_arr[] = { tul_const };      // TU局部:由constexpr TU局部对象构成的数组
struct tul_class { int mem; };
tul_class tul_obj{tul_const};       // TU局部:包含constexpr TU局部对象成员的结构体

Exposures

声明 D 命名 实体 E 如果

  1. D 包含一个闭包类型为 E 的 lambda 表达式,
  2. E 不是函数或函数模板,且 D 包含表示 E 的 id 表达式、类型说明符、嵌套名称说明符、模板名称或概念名称,或
  3. E 是函数或函数模板,且 D 包含命名 E 的表达式,或引用包含 E 的重载集合的 id 表达式。
// lambda 命名
auto x = [] {}; // 命名 decltype(x)
// 非函数(模板)命名
int y1 = 1;                      // 命名 y1(标识表达式)
struct y2 { int mem; };
y2 y2_obj{1};                    // 命名 y2(类型说明符)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // 命名 y3(嵌套名称说明符)
template<typename T> int y4 = 1;
int var = y4<y2>;                // 命名 y4(模板名称)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // 命名 y5(概念名称)
// 函数(模板)命名
int z1(int arg)    { std::cout << "no overload"; return 0; }
int z2(int arg)    { std::cout << "overload 1";  return 1; }
int z2(double arg) { std::cout << "overload 2";  return 2; }
int val1 = z1(0); // 命名 z1
int val2 = z2(0); // 命名 z2( int z2(int) )

声明在以下情况下属于 暴露 :它要么命名了TU局部实体(忽略

  1. 非内联函数或函数模板的函数体(但不包括使用 占位类型 声明返回类型的函数定义中(可能实例化的)推导返回类型),
  2. 变量或变量模板的初始化器(但不包括变量类型),
  3. 类定义中的友元声明,以及
  4. 任何对使用非常量表达式初始化的非易失性常量对象或具有内部链接或无链接的引用的引用(且不属于 odr-use ),

或定义一个初始化为 TU 本地值的 constexpr 变量。

翻译单元局部约束

如果模块接口单元(若存在私有模块片段,则在其外部)或模块分区中,对非翻译单元局部实体的(可能已实例化的) 声明 推导指南 构成暴露,则程序非良构。在任何其他上下文中的此类声明已被弃用。

若出现在某翻译单元中的声明,命名了在另一翻译单元中声明的非头文件单元的TU局部实体,则程序非良构。为模板特例化实例化的声明,将出现在该特例化的实例化点。

示例

翻译单元 #1:

export module A;
static void f() {}
inline void it() { f(); }         // 错误:暴露了 f
static inline void its() { f(); } // 正确
template<int> void g() { its(); } // 正确
template void g<0>();
decltype(f) *fp;                             // 错误:f(尽管其类型不是)是翻译单元局部
auto &fr = f;                                // 正确
constexpr auto &fr2 = fr;                    // 错误:暴露了 f
constexpr static auto fp2 = fr;              // 正确
struct S { void (&ref)(); } s{f};            // 正确:值是翻译单元局部
constexpr extern struct W { S &s; } wrap{s}; // 正确:值不是翻译单元局部
static auto x = []{ f(); }; // 正确
auto x2 = x;                // 错误:闭包类型是翻译单元局部
int y = ([]{ f(); }(), 0);  // 错误:闭包类型不是翻译单元局部
int y2 = (x, 0);            // 正确
namespace N
{
    struct A {};
    void adl(A);
    static void adl(int);
}
void adl(double);
inline void h(auto x) { adl(x); } // 正确,但特化可能成为暴露

翻译单元 #2:

module A;
void other()
{
    g<0>();                  // 正确:特化已显式实例化
    g<1>();                  // 错误:实例化使用了当前翻译单元局部的 its
    h(N::A{});               // 错误:重载集包含当前翻译单元局部的 N::adl(int)
    h(0);                    // 正确:调用 adl(double)
    adl(N::A{});             // 正确:未找到 N::adl(int),调用 N::adl(N::A)
    fr();                    // 正确:调用 f
    constexpr auto ptr = fr; // 错误:fr 在此处不能用于常量表达式
}