Translation-unit-local entities (since C++20)
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
-
ein Typ, eine Funktion, eine Variable oder ein Template, das
- einen Namen mit internal linkage hat, oder
- keinen Namen mit Linkage hat und innerhalb der Definition einer TU-lokalen Entität deklariert oder durch einen lambda expression eingeführt wird,
- 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,
- eine Spezialisierung eines TU-lokalen Templates,
- eine Spezialisierung eines Templates mit einem beliebigen TU-lokalen Template-Argument, oder
- 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; } };
|
Dieser Abschnitt ist unvollständig
Grund: Fehlende Beispiele für Regeln #1.2, #2 und #5 |
Ein Wert oder Objekt ist TU-lokal wenn entweder
- es ist, oder ein Zeiger auf, eine TU-lokale Funktion oder das Objekt, das mit einer TU-lokalen Variable assoziiert ist, oder
- 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
- D enthält einen Lambda-Ausdruck, dessen Closure-Typ E ist,
- E ist keine Funktion oder Funktionsvorlage und D enthält einen Id-Ausdruck, Typ-Spezifizierer, geschachtelten Namensspezifizierer, Vorlagennamen oder Konzeptnamen, der E bezeichnet, oder
- 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
- 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),
- der Initialisierer für eine Variable oder Variablenvorlage (aber nicht der Typ der Variable),
- friend-Deklarationen in einer Klassendefinition, und
- 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.
|
Dieser Abschnitt ist unvollständig
Grund: Fehlende Beispiele für Exposures |
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.
|
Dieser Abschnitt ist unvollständig
Grund: Fehlende Beispiele für Constraints |
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 }
|
Dieser Abschnitt ist unvollständig
Grund: Beispiele sind zu komplex, benötigen bessere Anordnung |