Language linkage
Proporciona enlace entre unidades de programa escritas en diferentes lenguajes de programación.
|
Esto también puede utilizarse para separar una declaración de su módulo. Consulte Module ownership . |
(since C++20) |
extern
string-literal
{
declaration-seq
(opcional)
}
|
(1) | ||||||||
extern
string-literal
declaration
|
(2) | ||||||||
| string-literal | - | un literal de cadena no evaluado que nombra el enlace de lenguaje requerido |
| declaration-seq | - | una secuencia de declaraciones, que puede incluir especificaciones de enlace anidadas |
| declaration | - | una declaración |
Contenidos |
Explicación
Cada tipo de función, cada nombre de función con enlace externo , y cada nombre de variable con enlace externo , tiene una propiedad llamada enlace de lenguaje . El enlace de lenguaje encapsula el conjunto de requisitos necesarios para enlazar con una unidad de programa escrita en otro lenguaje de programación: convención de llamada , algoritmo de alteración de nombres (decoración de nombres), etc.
Solo dos vinculaciones de lenguaje están garantizadas de ser compatibles:
- "C++" , el enlace de lenguaje predeterminado.
- "C" , que permite enlazar con funciones escritas en el lenguaje de programación C, y definir, en un programa C++, funciones que pueden ser llamadas desde unidades escritas en C.
extern "C" { int open(const char *path_name, int flags); // declaración de función C } int main() { int fd = open("test.txt", 0); // llama a una función C desde un programa C++ } // Esta función C++ puede ser llamada desde código C extern "C" void handler(int) { std::cout << "Callback invoked\n"; // Puede usar C++ }
Dado que el enlace de lenguaje es parte de cada tipo de función, los punteros a funciones también mantienen el enlace de lenguaje. El enlace de lenguaje de los tipos de función (que representa la convención de llamada) y el enlace de lenguaje de los nombres de función (que representa el name mangling) son independientes entre sí:
extern "C" void f1(void(*pf)()); // declara una función f1 con vinculación C, // que retorna void y toma un puntero a una función C // que retorna void y no toma parámetros extern "C" typedef void FUNC(); // declara FUNC como un tipo de función C que retorna void // y no toma parámetros FUNC f2; // el nombre f2 tiene vinculación C++, pero su tipo es función C extern "C" FUNC f3; // el nombre f3 tiene vinculación C y su tipo es función C void() void (*pf2)(FUNC*); // el nombre pf2 tiene vinculación C++, y su tipo es // "puntero a una función C++ que retorna void y toma un // argumento de tipo 'puntero a la función C que retorna void // y no toma parámetros'" extern "C" { static void f4(); // el nombre de la función f4 tiene vinculación interna (sin lenguaje) // pero el tipo de la función tiene vinculación de lenguaje C }
Si dos declaraciones de una entidad le asignan diferentes vinculaciones de lenguaje, el programa está mal formado; no se requiere diagnóstico si ninguna declaración es accesible desde la otra. Una redeclaración de una entidad sin una especificación de vinculación hereda la vinculación de lenguaje de la entidad y su tipo (si existe).
extern "C" int f(); extern "C++" int f(); // Error: enlaces de lenguaje diferentes extern "C" int g(); int g(); // OK, tiene enlace de lenguaje C int h(); // tiene enlace de lenguaje C++ por defecto extern "C" int h(); // Error: enlaces de lenguaje diferentes
Reglas especiales para enlace "C"
Cuando los miembros de clase , las funciones friend con una cláusula requires final , (desde C++20) o las funciones miembro no estáticas aparecen en un bloque de lenguaje "C" , la vinculación de sus tipos permanece "C++" (pero los tipos de parámetros, si los hay, permanecen "C" ):
extern "C" { class X { void mf(); // la función mf y su tipo tienen vinculación de lenguaje C++ void mf2(void(*)()); // la función mf2 tiene vinculación de lenguaje C++; // el parámetro tiene tipo "puntero a función C" }; } template<typename T> struct A { struct B; }; extern "C" { template<typename T> struct A<T>::B { friend void f(B*) requires true {} // vinculación de lenguaje C ignorada }; } namespace Q { extern "C" void f(); // no está mal formado }
Sea
C
una declaración que declara una función o variable con vinculación de lenguaje
"C"
. Si otra declaración
D
declara una entidad con el mismo nombre, y satisface cualquiera de las siguientes condiciones,
C
y
D
declaran la misma entidad:
-
Ddeclara una variable que pertenece al ámbito global. -
Si
Cdeclara una variable,Dtambién declara una variable. -
Si
Cdeclara una función,Dtambién declara una función.
A diferencia de las
redeclaraciones regulares
,
C
y
D
pueden tener diferentes
ámbitos de destino
:
extern "C" { int x; int f(); int g() { return 1; } } namespace A { int x; // Error: redefine «x» int f(); // OK, redeclara «f» int g() { return 1; } // Error: redefine «g» }
Sin embargo, las restricciones de tales declaraciones aún se aplican, lo que significa que ambas deben declarar funciones o ambas deben declarar variables, y las entidades declaradas deben tener el mismo tipo:
namespace A { extern "C" int x(); extern "C" int y(); } int x; // Error: redeclara "x" como un tipo de entidad diferente namespace B { void y(); // Error: redeclara "y" con un tipo diferente }
Notas
Las especificaciones de lenguaje solo pueden aparecer en el ámbito de espacio de nombres .
Las llaves de la especificación del lenguaje no establecen un ámbito.
Cuando las especificaciones de lenguaje se anidan, la especificación más interna es la que está en vigor.
Una declaración contenida directamente en una especificación de vinculación de lenguaje se trata como si contuviera el extern especificador para el propósito de determinar la vinculación del nombre declarado y si es una definición .
extern "C" int x; // una declaración y no una definición // La línea anterior es equivalente a extern "C" { extern int x; } extern "C" { int x; } // una declaración y definición extern "C" double f(); static double f(); // error: conflicto de vinculación extern "C" static void g(); // error: conflicto de vinculación
extern "C" permite incluir archivos de cabecera que contienen declaraciones de funciones de biblioteca C en un programa C++, pero si el mismo archivo de cabecera se comparte con un programa C, extern "C" (que no está permitido en C) debe ocultarse con un #ifdef apropiado, típicamente __cplusplus :
#ifdef __cplusplus extern "C" int foo(int, int); // El compilador C++ ve esto #else int foo(int, int); // El compilador C ve esto #endif
El único compilador moderno que diferencia tipos de función con "C" y "C++" enlaces de lenguaje es Oracle Studio; otros no permiten sobrecargas que solo difieran en el enlace de lenguaje, incluyendo los conjuntos de sobrecarga requeridos por el estándar de C++ ( std::qsort , std::bsearch , std::signal , std::atexit , y std::at_quick_exit ): GCC bug 2316 , Clang bug 6277 , CWG issue 1555 .
extern "C" using c_predfun = int(const void*, const void*); extern "C++" using cpp_predfun = int(const void*, const void*); // mal formado, pero aceptado por la mayoría de los compiladores static_assert(std::is_same<c_predfun, cpp_predfun>::value, "Los enlaces de lenguaje C y C++ no deben diferenciar tipos de función."); // las siguientes declaraciones no declaran sobrecargas en la mayoría de los compiladores // porque c_predfun y cpp_predfun se consideran del mismo tipo void qsort(void* base, std::size_t nmemb, std::size_t size, c_predfun* compar); void qsort(void* base, std::size_t nmemb, std::size_t size, cpp_predfun* compar);
Palabras clave
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 4 | C++98 | los nombres con enlace interno pueden tener enlaces de lenguaje | limitado a nombres con enlace externo |
| CWG 341 | C++98 |
una función con enlace de lenguaje
"C"
puede
tener el mismo nombre que una variable global |
el programa está mal formado en este caso
(no se requiere diagnóstico si aparecen en diferentes unidades de traducción) |
| CWG 564 | C++98 |
el programa estaba mal formado si dos declaraciones
solo diferían en las especificaciones de enlace de lenguaje (es decir, diferentes literales de cadena después de 'extern') |
se comparan los enlaces de lenguaje reales dados
por las declaraciones en su lugar |
| CWG 2460 | C++20 |
las funciones friend con una cláusula
requires
final
y enlace de lenguaje "C" tenían comportamientos conflictivos |
el enlace de lenguaje
"C"
se ignora en este caso |
| CWG 2483 | C++98 |
el enlace de los tipos de funciones miembro estáticas
que aparecen en bloques de lenguaje "C" era "C++" |
el enlace es "C" |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 9.11 Especificaciones de vinculación [dcl.link]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 9.11 Especificaciones de vinculación [dcl.link]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 10.5 Especificaciones de vinculación [dcl.link]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 7.5 Especificaciones de vinculación [dcl.link]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 7.5 Especificaciones de vinculación [dcl.link]
- Estándar C++03 (ISO/IEC 14882:2003):
-
- 7.5 Especificaciones de vinculación [dcl.link]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 7.5 Especificaciones de vinculación [dcl.link]