名稱空間
變體
操作

翻譯單元本地實體 (C++20起)

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

引入翻譯單元本地 (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 本地實體

一個實體是 *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; }
};

一個值或物件是 *TU 本地*的,如果

  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 包含一個其閉包型別為 E 的 lambda 表示式,
  2. E 不是函式或函式模板,且 D 包含一個表示 E 的 id-表示式、型別說明符、巢狀名稱說明符、模板名稱或概念名稱,或者
  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 本地實體,則它是一個 *暴露*,忽略

  1. 非行內函數或函式模板的函式體(但不是使用了 佔位符型別 的具有宣告返回型別的函式的(可能已例項化)定義的推導返回型別),
  2. 變數或變數模板的初始化器(但不是變數的型別),
  3. 類定義中的友元宣告,以及
  4. 任何對具有內部或無連結且用常量表達式初始化的非 volatile const 物件或引用的引用,該引用不是 ODR 使用

或定義一個初始化為 TU 本地值的 constexpr 變數。

[編輯] TU 本地限制

如果模組介面單元(在 private-module-fragment 之外,如果有)或模組分割槽中非 TU 本地實體的(可能已例項化)宣告推導指南 是一個暴露,則程式是病態的。在任何其他上下文中,此類宣告都已棄用。

如果一個翻譯單元中出現的宣告命名了在另一個不是頭單元的翻譯單元中宣告的 TU 本地實體,則程式是病態的。為模板特化例項化的宣告出現在特化的例項化點。

[編輯] 示例

翻譯單元 #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
}