翻译单元局部实体 (自 C++20 起)
来自 cppreference.cn
引入翻译单元局部 (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的,如果它是
- 类型、函数、变量或模板,其
- 名称具有内部链接,或
- 没有带链接的名称,并且在 TU-local 实体的定义中声明或由lambda 表达式引入,
- 一个没有名称的类型,它在类说明符、函数体或初始化器之外定义,或者由一个定义类型说明符(类型说明符、类说明符或枚举说明符)引入,该说明符仅用于声明 TU-local 实体,
- TU-local 模板的特化,
- 任何 TU-local 模板参数的模板的特化,或
- 其(可能实例化的)声明是暴露(定义如下)的模板的特化。
// 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; } };
本节尚不完整 原因:缺少规则 #1.2、#2 和 #5 的示例 |
一个值或对象是 TU-local 的,如果
- 它是 TU-local 函数或与 TU-local 变量关联的对象,或者是指向它们的指针,或者
- 它是一个类或数组类型的对象,并且它的任何子对象或它的引用类型的非静态数据成员引用的任何对象或函数是 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,则
- D 包含一个 lambda 表达式,其闭包类型为 E,
- E 不是函数或函数模板,并且 D 包含一个 id-表达式、类型说明符、嵌套名称说明符、模板名称或概念名称,表示 E,或者
- 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 实体,则该声明是一个暴露,忽略
- 非内联函数或函数模板的函数体(但不包括使用占位符类型的函数的(可能实例化的)定义的推导返回类型),
- 变量或变量模板的初始化器(但不包括变量的类型),
- 类定义中的友元声明,以及
- 对使用常量表达式初始化的具有内部链接或无链接的非易失性 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 }
本节尚不完整 原因:示例过于复杂,需要更好的安排 |