命名空间
变体
操作

翻译单元本地实体 (自 C++20 起)

来自 cppreference.com
< cpp‎ | 语言
 
 
C++ 语言
 
 

翻译单元本地 (TU-本地) 实体的引入是为了防止本来应该在本地(不在任何其他翻译单元中使用)的实体被公开并在其他翻译单元中使用。

来自 理解 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-本地实体

如果一个实体是:

  1. 类型、函数、变量或模板,并且:
    1. 具有 内部链接 的名称,或者
    2. 没有链接的名称,并且是在 TU-本地实体的定义内声明的,或者是由 lambda 表达式 引入的,
  2. 类说明符、函数体或初始化器之外定义的无名类型,或者是由用于声明仅 TU-本地实体的定义类型说明符(类型说明符、类说明符或枚举说明符)引入的,
  3. TU-本地模板的特化,
  4. 具有任何 TU-本地模板参数的模板的特化,或者
  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; }
};

如果一个值或对象是:

  1. 它是,或者是指向 TU-本地函数的指针,或者是指向与 TU-本地变量关联的对象的指针,或者
  2. 它是类或数组类型的对象,并且它的任何 子对象 或任何其非静态数据成员(引用类型)所引用的对象或函数是 TU-本地的,并且在 常量表达式中可用
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 包含表示 E 的标识符表达式、类型说明符、嵌套名称说明符、模板名称或概念名称,或者
  3. 如果 E 是函数或函数模板,而 D 包含命名 E 的表达式或引用包含 E 的重载集的标识符表达式。
// 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 本地实体,则它是一个 *公开*,忽略以下内容:

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

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

[edit] TU 本地约束

如果(可能实例化的)声明推断指南模块接口单元(在私有模块片段中,如果有的话)或模块分区中对非 TU 本地实体进行公开,则程序格式错误。在任何其他上下文中进行这样的声明都是不推荐的。

如果一个翻译单元中的声明命名了另一个翻译单元中声明的 TU 本地实体,而该实体不是头文件单元,则程序格式错误。为模板特化实例化的声明出现在特化的实例化点。

[edit] 示例

翻译单元 #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
}