Translation-unit-local entities (since C++20)
Les entités locales à l'unité de traduction (TU-locales) sont introduites pour empêcher que des entités censées être locales (non utilisées dans toute autre unité de traduction) soient exposées et utilisées dans d'autres unités de traduction.
Un exemple tiré de Understanding C++ Modules: Part 2 illustre le problème de ne pas contraindre les expositions :
// Module unit sans contraintes locales à l'unité de traduction export module Foo; import <iostream>; namespace { class LolWatchThis { // liaison interne, ne peut pas être exportée static void say_hello() { std::cout << "Hello, everyone!\n"; } }; } export LolWatchThis lolwut() { // LolWatchThis est exposé comme type de retour return LolWatchThis(); }
// main.cpp import Foo; int main() { auto evil = lolwut(); // 'evil' a le type 'LolWatchThis' decltype(evil)::say_hello(); // la définition de 'LolWatchThis' n'est plus interne }
Table des matières |
Entités locales à l'unité de traduction
Une entité est TU-local si elle est
-
un type, fonction, variable ou modèle qui
- possède un nom ayant une liaison interne , ou
- ne possède pas de nom avec liaison et est déclaré, ou introduit par une expression lambda , dans la définition d'une entité locale à l'unité de traduction,
- un type sans nom qui est défini en dehors d'un spécificateur de classe , corps de fonction ou initialiseur, ou est introduit par un spécificateur de type définissant (spécificateur de type, spécificateur de classe ou spécificateur d'énumération) utilisé pour déclarer uniquement des entités locales à l'unité de traduction,
- une spécialisation d'un modèle local à l'unité de traduction,
- une spécialisation d'un modèle avec tout argument de modèle local à l'unité de traduction, ou
- une spécialisation d'un modèle dont la déclaration (éventuellement instanciée) constitue une exposition (définie ci-dessous).
// Entités locales à l'unité de traduction avec liaison interne namespace { // tous les noms déclarés dans un espace de noms sans nom ont une liaison interne int tul_var = 1; // variable locale à l'unité de traduction int tul_func() { return 1; } // fonction locale à l'unité de traduction struct tul_type { int mem; }; // type (classe) local à l'unité de traduction } template<typename T> static int tul_func_temp() { return 1; } // template local à l'unité de traduction // Spécialisation de template locale à l'unité de traduction template<> static int tul_func_temp<int>() { return 3; } // spécialisation locale à l'unité de traduction // spécialisation de template avec argument de template local à l'unité de traduction template <> struct std::hash<tul_type> { // spécialisation locale à l'unité de traduction std::size_t operator()(const tul_type& t) const { return 4u; } };
|
Cette section est incomplète
Raison : manquent les exemples des règles #1.2, #2 et #5 |
Une valeur ou un objet est TU-local si l'une des conditions suivantes est remplie
- il s'agit, ou pointe vers, une fonction locale à l'unité de traduction ou l'objet associé à une variable locale à l'unité de traduction, ou
- il s'agit d'un objet de type classe ou tableau et l'un de ses sous-objets ou l'un des objets ou fonctions vers lesquels pointent ses membres de données non statiques de type référence est local à l'unité de traduction et est utilisable dans des expressions constantes .
static int tul_var = 1; // Variable locale à l'unité de traduction static int tul_func() { return 1; } // Fonction locale à l'unité de traduction int* tul_var_ptr = &tul_var; // Locale à l'unité de traduction : pointeur vers une variable locale à l'unité de traduction int (* tul_func_ptr)() = &tul_func; // Locale à l'unité de traduction : pointeur vers une fonction locale à l'unité de traduction constexpr static int tul_const = 1; // Variable locale à l'unité de traduction utilisable dans des expressions constantes int tul_arr[] = { tul_const }; // Locale à l'unité de traduction : tableau d'objets constexpr locaux à l'unité de traduction struct tul_class { int mem; }; tul_class tul_obj{tul_const}; // Locale à l'unité de traduction : possède un membre objet constexpr local à l'unité de traduction
Expositions
Une déclaration D nomme une entité E si
- D contient une expression lambda dont le type de fermeture est E,
- E n'est pas une fonction ou un modèle de fonction et D contient une expression-id, un spécificateur de type, un spécificateur de nom imbriqué, un nom de modèle ou un nom de concept désignant E, ou
- E est une fonction ou un modèle de fonction et D contient une expression qui nomme E ou une expression-id qui fait référence à un ensemble de surcharges contenant E.
// dénomination des lambda auto x = [] {}; // nomme decltype(x) // dénomination des non-fonctions (template) int y1 = 1; // nomme y1 (expression-id) struct y2 { int mem; }; y2 y2_obj{1}; // nomme y2 (spécificateur-de-type) struct y3 { int mem_func(); }; int y3::mem_func() { return 0; } // nomme y3 (spécificateur-de-nom-imbriqué) template<typename T> int y4 = 1; int var = y4<y2>; // nomme y4 (nom-de-template) template<typename T> concept y5 = true; template<typename T> void func(T&&) requires y5<T>; // nomme y5 (nom-de-concept) // dénomination des fonctions (template) 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); // nomme z1 int val2 = z2(0); // nomme z2 ( int z2(int) )
Une déclaration est une exposition si elle nomme soit une entité locale à l'unité de traduction, en ignorant
- le corps de fonction pour une fonction non inline ou un modèle de fonction (mais pas le type de retour déduit pour une définition (éventuellement instanciée) d'une fonction avec un type de retour déclaré qui utilise un type de substitution ),
- l'initialisateur pour une variable ou un modèle de variable (mais pas le type de la variable),
- les déclarations friend dans une définition de classe, et
- toute référence à un objet const non volatile ou référence avec liaison interne ou sans liaison initialisée avec une expression constante qui n'est pas une odr-use ,
ou définit une variable constexpr initialisée avec une valeur locale à l'unité de traduction.
|
Cette section est incomplète
Raison : manque d'exemples pour les expositions |
Contraintes locales à l'unité de traduction
Si une (éventuellement instanciée) déclaration de, ou un guide de déduction pour, une entité non-locale à l'unité de traduction dans une unité d'interface de module (en dehors du fragment privé du module, le cas échéant) ou une partition de module constitue une exposition, le programme est mal formé. Une telle déclaration dans tout autre contexte est dépréciée.
Si une déclaration qui apparaît dans une unité de traduction nomme une entité locale à l'unité de traduction déclarée dans une autre unité de traduction qui n'est pas une unité d'en-tête, le programme est mal formé. Une déclaration instanciée pour une spécialisation de modèle apparaît au point d'instanciation de la spécialisation.
|
Cette section est incomplète
Raison : exemples manquants pour les contraintes |
Exemple
Unité de traduction #1 :
export module A; static void f() {} inline void it() { f(); } // erreur : exposition de f static inline void its() { f(); } // OK template<int> void g() { its(); } // OK template void g<0>(); decltype(f) *fp; // erreur : f (bien que son type ne le soit pas) est local à l'unité de traduction auto &fr = f; // OK constexpr auto &fr2 = fr; // erreur : exposition de f constexpr static auto fp2 = fr; // OK struct S { void (&ref)(); } s{f}; // OK : la valeur est locale à l'unité de traduction constexpr extern struct W { S &s; } wrap{s}; // OK : la valeur n'est pas locale à l'unité de traduction static auto x = []{ f(); }; // OK auto x2 = x; // erreur : le type de fermeture est local à l'unité de traduction int y = ([]{ f(); }(), 0); // erreur : le type de fermeture n'est pas local à l'unité de traduction 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, mais une spécialisation pourrait être une exposition
Unité de traduction #2 :
module A; void other() { g<0>(); // OK : la spécialisation est explicitement instanciée g<1>(); // erreur : l'instanciation utilise son TU-local h(N::A{}); // erreur : l'ensemble de surcharge contient N::adl(int) TU-local h(0); // OK : appelle adl(double) adl(N::A{}); // OK ; N::adl(int) non trouvé, appelle N::adl(N::A) fr(); // OK : appelle f constexpr auto ptr = fr; // erreur : fr n'est pas utilisable dans les expressions constantes ici }
|
Cette section est incomplète
Motif : les exemples sont trop complexes, nécessitent un meilleur agencement |