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-lokale (TU-lokale) Entitäten wurden eingeführt, um zu verhindern, dass Entitäten, die lokal sein sollen (nicht in anderen Übersetzungseinheiten verwendet werden), in anderen Übersetzungseinheiten verfügbar gemacht und verwendet werden.

Ein Beispiel aus Understanding C++ Modules: Part 2 veranschaulicht das Problem fehlender Exportbeschränkungen:

// Modul-Einheit ohne TU-lokale Einschränkungen
export module Foo;
import <iostream>;
namespace
{
   class LolWatchThis {        // interne Verknüpfung, kann nicht exportiert werden
       static void say_hello()
       {
           std::cout << "Hello, everyone!\n";
       }
   };
}
export LolWatchThis lolwut() { // LolWatchThis wird als Rückgabetyp verfügbar gemacht
    return LolWatchThis();
}
// main.cpp
import Foo;
int main()
{
    auto evil = lolwut();        // 'evil' hat den Typ 'LolWatchThis'
    decltype(evil)::say_hello(); // Definition von 'LolWatchThis' ist nicht mehr intern
}

Inhaltsverzeichnis

TU-lokale Entitäten

Eine Entität ist TU-lokal , wenn sie

  1. ein Typ, eine Funktion, eine Variable oder ein Template, das
    1. einen Namen mit internal linkage hat, oder
    2. keinen Namen mit Linkage hat und innerhalb der Definition einer TU-lokalen Entität deklariert oder durch einen lambda expression eingeführt wird,
  2. ein Typ ohne Namen, der außerhalb eines class-specifier , Funktionskörpers oder Initialisierers definiert ist oder durch einen defining-type-specifier (type-specifier, class-specifier oder enum-specifier) eingeführt wird, der nur zur Deklaration von TU-lokalen Entitäten verwendet wird,
  3. eine Spezialisierung eines TU-lokalen Templates,
  4. eine Spezialisierung eines Templates mit einem beliebigen TU-lokalen Template-Argument, oder
  5. eine Spezialisierung eines Templates, dessen (möglicherweise instanziierte) Deklaration eine Exposure (wie unten definiert) ist.
// TU-lokale Entitäten mit interner Bindung
namespace { // alle Namen im unbenannten Namespace haben interne Bindung
    int tul_var = 1;                          // TU-lokale Variable
    int tul_func() { return 1; }              // TU-lokale Funktion
    struct tul_type { int mem; };             // TU-lokaler (Klassen-)Typ
}
template<typename T>
static int tul_func_temp() { return 1; }      // TU-lokale Template
// TU-lokale Template-Spezialisierung
template<>
static int tul_func_temp<int>() { return 3; } // TU-lokale Spezialisierung
// Template-Spezialisierung mit TU-lokalem Template-Argument
template <> struct std::hash<tul_type> {      // TU-lokale Spezialisierung
    std::size_t operator()(const tul_type& t) const { return 4u; }
};

Ein Wert oder Objekt ist TU-lokal wenn entweder

  1. es ist, oder ein Zeiger auf, eine TU-lokale Funktion oder das Objekt, das mit einer TU-lokalen Variable assoziiert ist, oder
  2. es ist ein Objekt vom Klassen- oder Array-Typ und eines seiner Subobjekte oder eines der Objekte oder Funktionen, auf die seine nicht-statischen Datenelemente vom Referenztyp verweisen, ist TU-lokal und ist in konstanten Ausdrücken verwendbar .
static int tul_var = 1;             // TU-lokale Variable
static int tul_func() { return 1; } // TU-lokale Funktion
int* tul_var_ptr = &tul_var;        // TU-lokal: Zeiger auf TU-lokale Variable
int (* tul_func_ptr)() = &tul_func; // TU-lokal: Zeiger auf TU-lokale Funktion
constexpr static int tul_const = 1; // TU-lokale Variable, verwendbar in konstanten Ausdrücken
int tul_arr[] = { tul_const };      // TU-lokal: Array eines constexpr TU-lokalen Objekts
struct tul_class { int mem; };
tul_class tul_obj{tul_const};       // TU-lokal: besitzt ein Mitglied mit constexpr TU-lokalem Objekt

Exposures

Eine Deklaration D benennt eine Entität E, wenn

  1. D enthält einen Lambda-Ausdruck, dessen Closure-Typ E ist,
  2. E ist keine Funktion oder Funktionsvorlage und D enthält einen Id-Ausdruck, Typ-Spezifizierer, geschachtelten Namensspezifizierer, Vorlagennamen oder Konzeptnamen, der E bezeichnet, oder
  3. E ist eine Funktion oder Funktionsvorlage und D enthält einen Ausdruck, der E benennt, oder einen Id-Ausdruck, der sich auf eine Überladungsmenge bezieht, die E enthält.
// Lambda-Namensgebung
auto x = [] {}; // benennt decltype(x)
// Nicht-Funktions-(Template)-Namensgebung
int y1 = 1;                      // benennt y1 (ID-Ausdruck)
struct y2 { int mem; };
y2 y2_obj{1};                    // benennt y2 (Typ-Spezifizierer)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // benennt y3 (geschachtelter-Namens-Spezifizierer)
template<typename T> int y4 = 1;
int var = y4<y2>;                // benennt y4 (Template-Name)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // benennt y5 (Konzept-Name)
// Funktions-(Template)-Namensgebung
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); // benennt z1
int val2 = z2(0); // benennt z2 ( int z2(int) )

Eine Deklaration ist ein Exposure , wenn sie entweder eine TU-lokale Entität benennt, unter Ignorierung

  1. der Funktionskörper für eine nicht-inline Funktion oder Funktionsvorlage (aber nicht der abgeleitete Rückgabetyp für eine (möglicherweise instanziierte) Definition einer Funktion mit einem deklarierten Rückgabetyp, der einen Platzhaltertyp verwendet),
  2. der Initialisierer für eine Variable oder Variablenvorlage (aber nicht der Typ der Variable),
  3. friend-Deklarationen in einer Klassendefinition, und
  4. jegliche Referenz auf ein nicht-flüchtiges const-Objekt oder eine Referenz mit interner oder ohne Verknüpfung, die mit einem konstanten Ausdruck initialisiert wird, der keine ODR-Verwendung ist,

oder definiert eine constexpr-Variable, die mit einem TU-lokalen Wert initialisiert wird.

TU-lokale Einschränkungen

Wenn eine (möglicherweise instanziierte) Deklaration von, oder ein Deduction Guide für, eine nicht-TU-lokale Entität in einer Modulschnittstelleneinheit (außerhalb des privaten Modulfragments, falls vorhanden) oder Modulpartition eine Exposition darstellt, ist das Programm fehlerhaft. Eine solche Deklaration in jedem anderen Kontext ist veraltet.

Wenn eine Deklaration, die in einer Übersetzungseinheit erscheint, eine TU-lokale Entität benennt, die in einer anderen Übersetzungseinheit deklariert ist, die keine Header-Einheit ist, ist das Programm fehlerhaft. Eine für eine Template-Spezialisierung instanziierte Deklaration erscheint am Punkt der Instanziierung der Spezialisierung.

Beispiel

Übersetzungseinheit #1:

export module A;
static void f() {}
inline void it() { f(); }         // Fehler: ist eine Offenlegung von f
static inline void its() { f(); } // OK
template<int> void g() { its(); } // OK
template void g<0>();
decltype(f) *fp;                             // Fehler: f (obwohl nicht sein Typ) ist TU-lokal
auto &fr = f;                                // OK
constexpr auto &fr2 = fr;                    // Fehler: ist eine Offenlegung von f
constexpr static auto fp2 = fr;              // OK
struct S { void (&ref)(); } s{f};            // OK: Wert ist TU-lokal
constexpr extern struct W { S &s; } wrap{s}; // OK: Wert ist nicht TU-lokal
static auto x = []{ f(); }; // OK
auto x2 = x;                // Fehler: der Closure-Typ ist TU-lokal
int y = ([]{ f(); }(), 0);  // Fehler: der Closure-Typ ist nicht TU-lokal
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, aber eine Spezialisierung könnte eine Offenlegung sein

Übersetzungseinheit #2:

module A;
void other()
{
    g<0>();                  // OK: Spezialisierung wird explizit instanziiert
    g<1>();                  // Fehler: Instanziierung verwendet TU-lokale its
    h(N::A{});               // Fehler: Überladungsmenge enthält TU-lokale N::adl(int)
    h(0);                    // OK: ruft adl(double) auf
    adl(N::A{});             // OK; N::adl(int) nicht gefunden, ruft N::adl(N::A) auf
    fr();                    // OK: ruft f auf
    constexpr auto ptr = fr; // Fehler: fr ist hier nicht in konstanten Ausdrücken verwendbar
}