Entidades locales a unidad de traducción(desde C++20)
Las entidades locales a unidad de traducción (local-UT) se introducen para prevenir que entidades que se suponen que son locales (no se usan en ninguna otra unidad de traducción) se vean expuestas y puedan ser usadas por otras unidades de traducción.
Un ejemplo en Understanding C++ Modules: Part 2 ilustra el problema de no limitar el acceso:
// Unidad de módulo sin restricciones locales-UT
export module Foo;
import <iostream>;
namespace {
class LolWatchThis { // enlace interno, no se puede exportar
static void di_hola() {
std::cout << "Hola a todos\n";
}
};
}
export LolWatchThis lolwut() { // Se expone LolWatchThis como tipo de retorno
return LolWatchThis();
}
// main.cpp
import Foo;
int main() {
auto malvado = lolwut(); // 'malvado' tiene el tipo de 'LolWatchThis'
decltype(malvado)::di_hola(); // la definición de 'LolWatchThis' ya no es interna
}
Entidades locales-UT
Una entidad es local-UT si es
- un tipo, función, variable, o plantilla que
- tiene un nombre con enlace interno, o
- no tiene un nombre con enlace y se declara o introduce mediante una expresión lambda, dentro de la definición de una entidad local-UT,
- un tipo sin nombre que se define fuera de un especificador de clase, un cuerpo de función, o un inicializador o se introduce mediante un especificador de tipo de definición (especificador de tipo, de clase o enumeración) que se usa para declarar solamente entidades locales-UT,
- una especialización de una plantilla local-UT,
- una especialización de una plantilla con cualquier argumento de plantilla local-UT, o
- una especialización de una plantilla cuya declaración (que puede estar instanciada) es una exposición (definido a continuación).
// Entidades locales-UT con enlace interno
namespace { // todos los nombres declarados en un espacio de nombres sin nombre tiene enlace interno
int tul_var = 1; // variable local-UT
int tul_func() { return 1; } // función local-UT
struct tul_tipo { int mem; }; // tipo (clase) local-UT
}
template<typename T>
static int tul_func_plan() { return 1; } // plantilla local-UT
// especialización de plantilla local-UT
template<>
static int tul_func_plan<int>() { return 3; } // especialización local-UT
// especialización de plantilla con argumento de plantilla local-UT
template <> struct std::hash<tul_tipo> { // especialización local-UT
std::size_t operator()(const tul_tipo& t) const { return 4u; }
};
| Esta sección está incompleta Razón: faltan ejemplos de las reglas #1.2, #2 y #5 |
Un valor u objeto es local-UT si
- es, o es puntero a, una función local-UT o el objeto asociado con una variable local-UT, o
- es un objeto de tipo clase o array y cualquiera de sus subobjetos o cualquiera de los objetos o funciones a los que se refieren sus miembros de datos no estáticos de tipo referencia es local-UT y se puede utilizar en expresiones constantes.
static int tul_var = 1; // variable local-UT
static int tul_func() { return 1; } // función local-UT
int* tul_var_ptr = &tul_var; // local-UT: puntero a variable local-UT
int (* tul_func_ptr)() = &tul_func; // local-UT: puntero a función local-UT
constexpr static int tul_const = 1; // variable local-UT que se puede usar en expresiones constantes
int tul_arr[] = { tul_const }; // local-UT : array de objeto constexpr local-UT
struct tul_clase { int mem; };
tul_clase tul_obj{tul_const}; // local-UT : tiene miembro objeto constexpr local-UT
Exposiciones
Una declaración D denomina una entidad E si
- D contiene una expresión lambda cuyo tipo de cierra es E,
- E no es una función o plantilla de función y D contiene una expresión id, especificador de tipo, especificador de nombre anidado, nombre de plantilla, o nombre de concepto que denota E, o
- E es una función o plantilla de función y D contiene una expresión que nombre E o una expresión id que se refiere al conjunto de sobrecargas que contienen E.
// nomenclatura lambda
auto x = [] {}; // denomina decltype(x)
// nomenclatura de no función (plantilla)
int y1 = 1; // denomina y1 (expresión id)
struct y2 { int mem; };
y2 y2_obj{1}; // denomina y2 (especificador de tipo)
struct y3 { int mem_func(); };
int y3::mem_func() { return 0; } // denomina y3 (especificador de nombre anidado)
template<typename T> int y4 = 1;
int var = y4<y2>; // denomina y4 (nombre de plantilla)
template<typename T> concept y5 = true;
template<typename T> void func(T&&) requires y5<T>; // denomina y5 (nombre de concepto)
// nomenclatura de función (plantilla)
int z1(int arg) { std::cout << "no 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); // denomina z1
int val2 = z2(0); // denomina z2 ( int z2(int) )
Una declaración es una exposición si nombra una entidad local-UT, ignorando
- el cuerpo de función para una función no inline o plantilla de función (pero no el tipo de retorno deducido para una definición (que puede estar instanciado) de una función con un tipo de retorno declarado que usa un tipo deducido),
- el inicializador de una variable o plantilla de variable (pero no el tipo de la variable),
- declaraciones amigas en una definición de clase, y
- cualquier referencia a un objeto constante no volátil o referencia con o sin enlazado interno inicializada con una expresión constante que no es de uso ODR, o define una variable constexpr inicializada a una valor local-UT.
| Esta sección está incompleta Razón: faltas ejemplos para exposiciones |
Restricciones local-UT
Si una declaración (que puede estar instanciada) de, o una guía de deducción para, una entidad no local-UT en una unidad de interfaz de módulo (fuera del fragmento de módulo privado) o partición de módulo es una exposición, el programa está mal formado. Tal declaración en cualquier otro contexto está en desuso.
Si una declaración que aparece en una unidad de traducción nombra una entidad local-UT 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.
| Esta sección está incompleta Razón: falta ejemplos para restricciones |
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(); } // correcto
template<int> void g() { its(); } // correcto
template void g<0>();
decltype(f) *fp; // error: f (aunque no es su tipo) es local-UT
auto &fr = f; // correcto
constexpr auto &fr2 = fr; // error: es una exposición de f
constexpr static auto fp2 = fr; // correcto
struct S { void (&ref)(); } s{f}; // correcto: el valor es local-UT
constexpr extern struct W { S &s; } wrap{s}; // correcto: el valor no es local-UT
static auto x = []{f();}; // correcto
auto x2 = x; // error: el tipo de cierre es local-UT
int y = ([]{f();}(),0); // error: el tipo de cierre no es local-UT
int y2 = (x,0); // correcto
namespace N {
struct A {};
void adl(A);
static void adl(int);
}
void adl(double);
inline void h(auto x) { adl(x); } // Correcto, pero una especialización podría se una exposición
Unidad de traducción #2:
module A;
void other() {
g<0>(); // correcto: la especialización se instancia explicitamente
g<1>(); // error: la instanciación usa su local-UT
h(N::A{}); // error: el conjunto de sobrecargas contiene N::adl(int) local-UT
h(0); // correcto: llama a adl(double)
adl(N::A{}); // correcto; no encuentra N::adl(int), llama a N::adl(N::A)
fr(); // correcto: llama a f
constexpr auto ptr = fr; // error: aquí no se puede usar fr en expresiones constantes
}
| Esta sección está incompleta Razón: ejemplos demasiado complejos |