studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones c++ templates extern linkage

c++ - programacion - ¿Por qué las plantillas no pueden estar dentro de bloques "C" externos?



manual de programacion android pdf (5)

¿Qué significa que una plantilla "puede" tener un vínculo? ¿Qué es el enlace de la plantilla?

Todos los nombres tienen enlaces externos, enlaces internos o no tienen ningún vínculo (C ++ 03 §3.5p2), pero este no es el mismo vínculo que el enlace de idiomas. (Es confuso, lo sé. C ++ 0x también cambia considerablemente las cosas con el enlace). Se requiere vinculación externa para todo lo que se use como argumento de plantilla:

void f() { struct S {}; vector<S> v; // Not allowed as S has internal linkage. }

Observe que C ++ 98 tiene "puede" en lo que citó de §14p4, pero C ++ 03 elimina el "mayo", ya que las plantillas no se pueden declarar en un contexto que les dé un enlace interno:

void f() { // Not allowed: template<class T> struct S {}; }

Esta es una pregunta de seguimiento para una respuesta a ¿Es posible escribir un tipo de función de puntero a externo? "C" dentro de una plantilla?

Este código no compila con g++ , Visual C / C ++ y Comeau C / C ++ con básicamente el mismo mensaje de error:

#include <cstdlib> extern "C" { static int do_stuff(int) { return 3; } template <typename return_t_, typename arg1_t_> struct test { static void foo(return_t_ (*)(arg1_t_)) { } }; } int main() { test<int, int>::foo(&do_stuff); return EXIT_SUCCESS; }

g ++ dice "error: plantilla con enlace C", Visual C / C ++ emite el error de compilación C2894 , y Comeau C / C ++ dice "error: esta declaración puede no tener un enlace externo" C ".

El caso es que todos están contentos con:

#include <cstdlib> extern "C" { static int do_stuff(int) { return 3; } struct test { static void foo(int (*)(int)) { } }; } int main() { test::foo(&do_stuff); return EXIT_SUCCESS; }

La Sección 7.5, Especificaciones de vinculación, del Estándar C ++ establece:

La vinculación del lenguaje AC se ignora para los nombres de los miembros de la clase y el tipo de función miembro de las funciones miembro de la clase.

E incluso da el ejemplo:

extern "C" { class X { void mf(); // the name of the function mf and the member // function''s type have C++ language linkage void mf2(void(*)()); // the name of the function mf2 has C++ language // linkage; the parameter has type pointer to C function }; }

Si se permitieran plantillas en bloques Extern "C", entonces las funciones miembro de las instancias tendrían un enlace C ++.

¿Por qué, entonces, el capítulo 14, Plantillas, del estado estándar de C ++ 98:

El nombre de una plantilla puede tener un enlace (3.5). Una plantilla, una especialización explícita de plantilla (14.7.3) y una especialización parcial de plantilla de clase no tendrán un enlace C.

¿Qué significa que una plantilla "puede" tener un vínculo? ¿Qué es el enlace de la plantilla?

¿Por qué está explícitamente prohibido tener una plantilla con enlace C, cuando una clase está bien, y todas las funciones miembro de las instancias de la plantilla (el constructor predeterminado, el destructor y la sobrecarga del operador de asignación) tendrían un enlace C ++?


Debido a que los nombres de las funciones de la plantilla deben decorarse con información adicional, y la extern "C" desactiva la decoración. El objetivo de la extern "C" es poder declarar las funciones que se pueden invocar con el enlace C, que es algo que nunca funcionará obviamente con una función de plantilla.


Las plantillas no son códigos reales, solo son pautas para el compilador sobre cómo generar el código una vez que se conocen los parámetros de la plantilla. Como tal, en realidad no existen hasta que intentes usarlos. No puede proporcionar enlaces a algo que no existe.


Porque no hay plantillas en C.


Debido a que el extern C inhabilita el nombre que cambia qué plantillas usan

Para ver que las plantillas se implementan con el nombre de manipulación, compilación y descompilación:

#include <cassert> template <class C> C f(C i) { return i; } int main() { f<int>(1); f<double>(1.5); }

con:

g++ -c -g -std=c++98 main.cpp objdump -Sr main.o

La salida contiene:

int main() { 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp f<int>(1); 8: bf 01 00 00 00 mov $0x1,%edi d: e8 00 00 00 00 callq 12 <main+0x12> e: R_X86_64_PC32 _Z1fIiET_S0_-0x4 f<double>(1.5); 12: 48 b8 00 00 00 00 00 movabs $0x3ff8000000000000,%rax 19: 00 f8 3f 1c: 48 89 45 f8 mov %rax,-0x8(%rbp) 20: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0 25: e8 00 00 00 00 callq 2a <main+0x2a> 26: R_X86_64_PC32 _Z1fIdET_S0_-0x4 } 2a: b8 00 00 00 00 mov $0x0,%eax 2f: c9 leaveq 30: c3 retq

Observe cómo se hicieron todas las callq para llamar a nombres raros como _Z1fIiET_S0_ .

Lo mismo ocurre con otras características que dependen del cambio de nombre, por ejemplo, sobrecarga de funciones.

Ver también: en fuente C ++, ¿cuál es el efecto de la "C" externa?