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

Translation-unit-local (TU-local) エンティティは、ローカルであるべき(他の翻訳単位で使用されない)エンティティが他の翻訳単位で公開・使用されるのを防ぐために導入されました。

Understanding C++ Modules: Part 2 からの例は、エクスポートの制約を設けない場合の問題点を示しています:

// TUローカル制約のないモジュールユニット
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-localエンティティの定義内で宣言されるか、 lambda式 によって導入されるもの
  2. class-specifier 、関数本体、または初期化子の外側で定義される無名の型、またはTU-localエンティティのみを宣言するために使用される定義型指定子(type-specifier、class-specifier、またはenum-specifier)によって導入される無名の型
  3. TU-localテンプレートの特殊化
  4. TU-localテンプレート引数を1つ以上含むテンプレートの特殊化
  5. その(インスタンス化された可能性のある)宣言がexposure(後述で定義)であるテンプレートの特殊化
// 内部リンケージを持つ翻訳単位ローカルなエンティティ
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ローカルオブジェクトのメンバを持つ

エクスポージャー

宣言Dは、以下の場合にエンティティEを 命名する

  1. D がそのクロージャ型が E であるラムダ式を含む場合、
  2. E が関数または関数テンプレートではなく、かつ D が E を表す id-expression、type-specifier、nested-name-specifier、template-name、または concept-name を含む場合、または
  3. E が関数または関数テンプレートであり、かつ D が E を名前とする式、または E を含むオーバーロードの集合を参照する id-expression を含む場合。
// ラムダの命名
auto x = [] {}; // decltype(x) を命名
// 非関数(テンプレート)の命名
int y1 = 1;                      // y1 を命名(id-expression)
struct y2 { int mem; };
y2 y2_obj{1};                    // y2 を命名(type-specifier)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // y3 を命名(nested-name-specifier)
template<typename T> int y4 = 1;
int var = y4<y2>;                // y4 を命名(template-name)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // y5 を命名(concept-name)
// 関数(テンプレート)の命名
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) )

宣言は、以下のいずれかの場合に exposure となります:TU-localエンティティに名前を付ける場合、無視して

  1. 非インライン関数または関数テンプレートの関数本体(ただし、宣言された戻り値の型が プレースホルダ型 を使用する関数の(インスタンス化された可能性のある)定義における推論された戻り値の型は除く)、
  2. 変数または変数テンプレートの初期化子(ただし変数の型は除く)、
  3. クラス定義内のfriend宣言、および
  4. 定数式で初期化され、 odr-use ではない、内部リンケージまたはリンケージなしの非volatile constオブジェクトまたは参照への任意の参照、

または、TU-local値で初期化されたconstexpr変数を定義します。

TUローカル制約

(インスタンス化されている可能性のある) declaration または deduction guide が、非TU-localエンティティに対するものであり、 module interface unit (private-module-fragmentが存在する場合はその外部) またはモジュールパーティション内でexposureである場合、プログラムはill-formedとなる。そのような宣言が他のいかなる文脈で行われた場合でも、非推奨とされる。

ある翻訳単位に現れる宣言が、ヘッダーユニットではない別の翻訳単位で宣言されたTU-localエンティティを指名する場合、そのプログラムは不適格である。テンプレート特殊化に対してインスタンス化された宣言は、その特殊化のインスタンス化点に現れる。

翻訳単位 #1:

export module A;
static void f() {}
inline void it() { f(); }         // エラー: fの露出となる
static inline void its() { f(); } // OK
template<int> void g() { its(); } // OK
template void g<0>();
decltype(f) *fp;                             // エラー: f(その型ではない)はTUローカル
auto &fr = f;                                // OK
constexpr auto &fr2 = fr;                    // エラー: fの露出となる
constexpr static auto fp2 = fr;              // OK
struct S { void (&ref)(); } s{f};            // OK: 値はTUローカル
constexpr extern struct W { S &s; } wrap{s}; // OK: 値はTUローカルではない
static auto x = []{ f(); }; // OK
auto x2 = x;                // エラー: クロージャ型はTUローカル
int y = ([]{ f(); }(), 0);  // エラー: クロージャ型はTUローカルではない
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、ただし特殊化は露出となる可能性がある

翻訳単位 #2:

module A;
void other()
{
    g<0>();                  // OK: 特殊化は明示的にインスタンス化されている
    g<1>();                  // エラー: インスタンス化は翻訳単位ローカルなitsを使用する
    h(N::A{});               // エラー: オーバーロードセットに翻訳単位ローカルなN::adl(int)が含まれる
    h(0);                    // OK: adl(double)を呼び出す
    adl(N::A{});             // OK; N::adl(int)は見つからず、N::adl(N::A)を呼び出す
    fr();                    // OK: fを呼び出す
    constexpr auto ptr = fr; // エラー: frはここでは定数式で使用できない
}