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

Las entidades locales de unidad de traducción (TU-local) se introducen para evitar que entidades que se supone que son locales (no utilizadas en ninguna otra unidad de traducción) sean expuestas y utilizadas en otras unidades de traducción.

Un ejemplo de Understanding C++ Modules: Part 2 ilustra el problema de no restringir las exposiciones:

// Módulo sin restricciones locales de TU
export module Foo;
import <iostream>;
namespace
{
   class LolWatchThis {        // enlace interno, no puede ser exportado
       static void say_hello()
       {
           std::cout << "Hello, everyone!\n";
       }
   };
}
export LolWatchThis lolwut() { // LolWatchThis se expone como tipo de retorno
    return LolWatchThis();
}
// main.cpp
import Foo;
int main()
{
    auto evil = lolwut();        // 'evil' tiene tipo 'LolWatchThis'
    decltype(evil)::say_hello(); // la definición de 'LolWatchThis' ya no es interna
}

Contenidos

Entidades locales de la unidad de traducción

Una entidad es TU-local si es

  1. un tipo, función, variable o plantilla que
    1. tiene un nombre con internal linkage , o
    2. no tiene un nombre con linkage y es declarado, o introducido por una lambda expression , dentro de la definición de una entidad local a la unidad de traducción,
  2. un tipo sin nombre que se define fuera de un class-specifier , cuerpo de función o inicializador, o es introducido por un defining-type-specifier (type-specifier, class-specifier o enum-specifier) que se utiliza para declarar únicamente entidades locales a la unidad de traducción,
  3. una especialización de una plantilla local a la unidad de traducción,
  4. una especialización de una plantilla con cualquier argumento de plantilla local a la unidad de traducción, o
  5. una especialización de una plantilla cuya declaración (posiblemente instanciada) es una exposición (definida a continuación).
// Entidades locales a la TU con enlace interno
namespace { // todos los nombres declarados en el espacio de nombres sin nombre tienen enlace interno
    int tul_var = 1;                          // variable local a la TU
    int tul_func() { return 1; }              // función local a la TU
    struct tul_type { int mem; };             // tipo (clase) local a la TU
}
template<typename T>
static int tul_func_temp() { return 1; }      // plantilla local a la TU
// Especialización de plantilla local a la TU
template<>
static int tul_func_temp<int>() { return 3; } // especialización local a la TU
// especialización de plantilla con argumento de plantilla local a la TU
template <> struct std::hash<tul_type> {      // especialización local a la TU
    std::size_t operator()(const tul_type& t) const { return 4u; }
};

Un valor u objeto es TU-local si se cumple alguna de las siguientes condiciones:

  1. es, o es un puntero a, una función TU-local o el objeto asociado con una variable TU-local, o
  2. es un objeto de tipo clase o array y cualquiera de sus subobjects o cualquiera de los objetos o funciones a los que se refieren sus miembros de datos no estáticos de tipo referencia es TU-local y es usable en constant expressions .
static int tul_var = 1;             // Variable local a la TU
static int tul_func() { return 1; } // Función local a la TU
int* tul_var_ptr = &tul_var;        // Local a la TU: puntero a variable local de la TU
int (* tul_func_ptr)() = &tul_func; // Local a la TU: puntero a función local de la TU
constexpr static int tul_const = 1; // Variable local a la TU utilizable en expresiones constantes
int tul_arr[] = { tul_const };      // Local a la TU: arreglo de objeto constexpr local a la TU
struct tul_class { int mem; };
tul_class tul_obj{tul_const};       // Local a la TU: tiene miembro de objeto constexpr local a la TU

Exposures

Una declaración D nombra una entidad E si

  1. D contiene una expresión lambda cuyo tipo de cierre es E,
  2. E no es una función o plantilla de función y D contiene una expresión de identificación, especificador de tipo, especificador de nombre anidado, nombre de plantilla o nombre de concepto que denota E, o
  3. E es una función o plantilla de función y D contiene una expresión que nombra E o una expresión de identificación que se refiere a un conjunto de sobrecargas que contiene E.
// nombrado de lambda
auto x = [] {}; // nombra decltype(x)
// nombrado de no-función (plantilla)
int y1 = 1;                      // nombra y1 (expresión-id)
struct y2 { int mem; };
y2 y2_obj{1};                    // nombra y2 (especificador-de-tipo)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // nombra y3 (especificador-de-nombre-anidado)
template<typename T> int y4 = 1;
int var = y4<y2>;                // nombra y4 (nombre-de-plantilla)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // nombra y5 (nombre-de-concepto)
// nombrado de función (plantilla)
int z1(int arg)    { std::cout << "sin sobrecarga"; return 0; }
int z2(int arg)    { std::cout << "sobrecarga 1";  return 1; }
int z2(double arg) { std::cout << "sobrecarga 2";  return 2; }
int val1 = z1(0); // nombra z1
int val2 = z2(0); // nombra z2 ( int z2(int) )

Una declaración es una exposición si nombra una entidad local a la unidad de traducción, ignorando

  1. el cuerpo de la función para una función no inline o plantilla de función (pero no el tipo de retorno deducido para una definición (posiblemente instanciada) de una función con un tipo de retorno declarado que utiliza un tipo de marcador de posición ),
  2. el inicializador para una variable o plantilla de variable (pero no el tipo de la variable),
  3. declaraciones friend en una definición de clase, y
  4. cualquier referencia a un objeto const no volátil o referencia con enlace interno o sin enlace inicializada con una expresión constante que no es un odr-use ,

o define una variable constexpr inicializada con un valor local a la TU.

Restricciones locales de la unidad de traducción

Si una (posiblemente instanciada) declaración de, o una guía de deducción para, una entidad no-local-a-la-TU en una unidad de interfaz de módulo (fuera del fragmento-privado-del-módulo, si existe) o partición de módulo es una exposición, el programa está mal formado. Tal declaración en cualquier otro contexto está obsoleta.

Si una declaración que aparece en una unidad de traducción nombra una entidad local a la TU declarada en otra unidad de traducción que no es una unidad de cabecera, el programa está mal formado. Una declaración instanciada para una especialización de plantilla aparece en el punto de instanciación de la especialización.

Ejemplo

Unidad de traducción #1:

export module A;
static void f() {}
inline void it() { f(); }         // error: es una exposición de f
static inline void its() { f(); } // OK
template<int> void g() { its(); } // OK
template void g<0>();
decltype(f) *fp;                             // error: f (aunque no su tipo) es TU-local
auto &fr = f;                                // OK
constexpr auto &fr2 = fr;                    // error: es una exposición de f
constexpr static auto fp2 = fr;              // OK
struct S { void (&ref)(); } s{f};            // OK: el valor es TU-local
constexpr extern struct W { S &s; } wrap{s}; // OK: el valor no es TU-local
static auto x = []{ f(); }; // OK
auto x2 = x;                // error: el tipo de cierre es TU-local
int y = ([]{ f(); }(), 0);  // error: el tipo de cierre no es 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, pero una especialización podría ser una exposición

Unidad de traducción #2:

module A;
void other()
{
    g<0>();                  // OK: la especialización está explícitamente instanciada
    g<1>();                  // error: la instanciación utiliza su TU-local
    h(N::A{});               // error: el conjunto de sobrecarga contiene N::adl(int) TU-local
    h(0);                    // OK: llama a adl(double)
    adl(N::A{});             // OK; N::adl(int) no encontrada, llama a N::adl(N::A)
    fr();                    // OK: llama a f
    constexpr auto ptr = fr; // error: fr no es utilizable en expresiones constantes aquí
}