命名空间
变体
操作

翻译单元局部实体 (自 C++20 起)

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句 (循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (在 C++17* 中弃用)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

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

来自 理解 C++ 模块:第 2 部分 的一个例子说明了不约束暴露的问题

// Module unit without TU-local constraints
export module Foo;
 
import <iostream>;
 
namespace
{
   class LolWatchThis {        // internal linkage, cannot be exported
       static void say_hello()
       {
           std::cout << "Hello, everyone!\n";
       }
   };
}
 
export LolWatchThis lolwut() { // LolWatchThis is exposed as return type
    return LolWatchThis();
}
// main.cpp
import Foo;
 
int main()
{
    auto evil = lolwut();        // 'evil' has type of 'LolWatchThis'
    decltype(evil)::say_hello(); // definition of 'LolWatchThis' is not internal anymore
}

目录

[编辑] TU-local 实体

一个实体是TU-local的,如果它是

  1. 类型、函数、变量或模板,其
    1. 名称具有内部链接,或
    2. 没有带链接的名称,并且在 TU-local 实体的定义中声明或由lambda 表达式引入,
  2. 一个没有名称的类型,它在类说明符、函数体或初始化器之外定义,或者由一个定义类型说明符(类型说明符、类说明符或枚举说明符)引入,该说明符仅用于声明 TU-local 实体,
  3. TU-local 模板的特化,
  4. 任何 TU-local 模板参数的模板的特化,或
  5. 其(可能实例化的)声明是暴露(定义如下)的模板的特化。
// TU-local entities with internal linkage
namespace { // all names declared in unnamed namespace have internal linkage
    int tul_var = 1;                          // TU-local variable
    int tul_func() { return 1; }              // TU-local function
    struct tul_type { int mem; };             // TU-local (class) type
}
template<typename T>
static int tul_func_temp() { return 1; }      // TU-local template
 
// TU-local template specialization
template<>
static int tul_func_temp<int>() { return 3; } // TU-local specialization
 
// template specialization with TU-local template argument
template <> struct std::hash<tul_type> {      // TU-local specialization
    std::size_t operator()(const tul_type& t) const { return 4u; }
};

一个值或对象是 TU-local 的,如果

  1. 它是 TU-local 函数或与 TU-local 变量关联的对象,或者是指向它们的指针,或者
  2. 它是一个类或数组类型的对象,并且它的任何子对象或它的引用类型的非静态数据成员引用的任何对象或函数是 TU-local 的,并且在常量表达式中可用
static int tul_var = 1;             // TU-local variable
static int tul_func() { return 1; } // TU-local function
 
int* tul_var_ptr = &tul_var;        // TU-local: pointer to TU-local variable
int (* tul_func_ptr)() = &tul_func; // TU-local: pointer to TU-local function
 
constexpr static int tul_const = 1; // TU-local variable usable in constant expressions
int tul_arr[] = { tul_const };      // TU-local: array of constexpr TU-local object 
struct tul_class { int mem; };
tul_class tul_obj{tul_const};       // TU-local: has member constexpr TU-local object

[编辑] 暴露

如果声明 D 命名 实体 E,则

  1. D 包含一个 lambda 表达式,其闭包类型为 E,
  2. E 不是函数或函数模板,并且 D 包含一个 id-表达式、类型说明符、嵌套名称说明符、模板名称或概念名称,表示 E,或者
  3. E 是函数或函数模板,并且 D 包含一个命名 E 的表达式或一个引用包含 E 的重载集合的 id-表达式。
// lambda naming
auto x = [] {}; // names decltype(x)
 
// non-function (template) naming
int y1 = 1;                      // names y1 (id-expression)
struct y2 { int mem; };
y2 y2_obj{1};                    // names y2 (type-specifier)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // names y3 (nested-name-specifier)
template<typename T> int y4 = 1;
int var = y4<y2>;                // names y4 (template-name)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // names y5 (concept-name)
 
// function (template) naming
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); // names z1
int val2 = z2(0); // names z2 ( int z2(int) )

如果一个声明命名了一个 TU-local 实体,则该声明是一个暴露,忽略

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

或者定义一个初始化为 TU-local 值的 constexpr 变量。

[编辑] TU-local 约束

如果在模块接口单元(在私有模块片段之外,如果有的话)或模块分区中,非 TU-local 实体的(可能实例化的)声明类模板的推导指南是暴露,则程序是非良构的。 在任何其他上下文中,此类声明都是已弃用的。

如果出现在一个翻译单元中的声明命名了在另一个不是头文件单元的翻译单元中声明的 TU-local 实体,则程序是非良构的。 为模板特化实例化的声明出现在特化的实例化点。

[编辑] 示例

翻译单元 #1

export module A;
static void f() {}
inline void it() { f(); }         // error: is an exposure of f
static inline void its() { f(); } // OK
template<int> void g() { its(); } // OK
template void g<0>();
 
decltype(f) *fp;                             // error: f (though not its type) is TU-local
auto &fr = f;                                // OK
constexpr auto &fr2 = fr;                    // error: is an exposure of f
constexpr static auto fp2 = fr;              // OK
struct S { void (&ref)(); } s{f};            // OK: value is TU-local
constexpr extern struct W { S &s; } wrap{s}; // OK: value is not TU-local
 
static auto x = []{ f(); }; // OK
auto x2 = x;                // error: the closure type is TU-local
int y = ([]{ f(); }(), 0);  // error: the closure type is not TU-local
int y2 = (x, 0);            // OK
 
namespace N
{
    struct A {};
    void adl(A);
    static void adl(int);
}
void adl(double);
 
inline void h(auto x) { adl(x); } // OK, but a specialization might be an exposure

翻译单元 #2

module A;
void other()
{
    g<0>();                  // OK: specialization is explicitly instantiated
    g<1>();                  // error: instantiation uses TU-local its
    h(N::A{});               // error: overload set contains TU-local N::adl(int)
    h(0);                    // OK: calls adl(double)
    adl(N::A{});             // OK; N::adl(int) not found, calls N::adl(N::A)
    fr();                    // OK: calls f
    constexpr auto ptr = fr; // error: fr is not usable in constant expressions here
}