una todas tipos llamar las funciones funcion estructura ejemplos dev como c++ templates c++14 name-binding template-instantiation

todas - tipos de funciones en c++



¿En qué punto se produce la vinculación de instancias de plantilla? (1)

Este código es del "lenguaje de programación C ++" de Bjarne Stroustrup (C.13.8.3 Punto de enlace de instanciación)

template <class T> void f(T value) { g(value); } void g(int v); void h() { extern g(double); f(2); }

Y menciona:

Aquí, el punto de creación de instancias para f () es justo antes de h (), por lo que g () llamado en f () es la g (int) global en lugar de la g (doble) local. La definición de "punto de instanciación" implica que un parámetro de plantilla nunca puede vincularse a un nombre local o un miembro de clase.

void h() { struct X {}; // local structure std::vector<X> v; // error: can''t use local structure as template parameter }

Mis preguntas son:

  1. ¿Por qué debería funcionar el primer código? g() se declara más tarde, y realmente obtengo un error con G ++ 4.9.2 que g no se declara en ese momento.

  2. extern g (doble) - ¿Cómo funciona esto? ¿Ya que el valor de retorno no importa en caso de una sobrecarga de funciones, podemos perderlo en las declaraciones a futuro?

  3. el punto de instanciación para f () es justo antes de h () : ¿por qué? ¿no es lógico que se ejecute cuando se llame a f(2) ? Justo donde lo llamamos, desde donde g(double) ya estará dentro del alcance.

  4. La definición de "punto de instanciación" implica que un parámetro de plantilla nunca puede vincularse a un nombre local o a un miembro de la clase . ¿Ha cambiado esto en C ++ 14? Recibo un error con C ++ (G ++ 4.9.2), pero no obtengo un error con C ++ 14 (G ++ 4.9.2).


"En 1985, se lanzó la primera edición de The C ++ Programming Language, que se convirtió en la referencia definitiva para el idioma, ya que aún no existía un estándar oficial ". wiki Así que no cambió entre C ++ 11 y C ++ 14. Puedo asumir (y por favor tome esto con un grano de sal) que cambió entre "pre-estandarización" y estandarización. Tal vez alguien que conozca mejor la historia de C ++ pueda arrojar más luz aquí.

En cuanto a lo que realmente sucede:

Primero salgamos de la manera simple:

extern g(double);

Esto no es válido en C ++. Históricamente, desafortunadamente C permitía omisiones de tipo. En C ++ tienes que escribir extern void g(double) .

A continuación, ignoremos la sobrecarga de g(double) para responder tu primera pregunta:

template <class T> void f(T value) { g(value); } void g(int v); int main() { f(2); }

En C ++ hay la búsqueda de nombre de dos fases infame:

  • En la primera fase, en la definición de la plantilla, se resuelven todos los nombres no dependientes . El no hacerlo es un error duro;
  • Los nombres de los dependientes se resuelven en la fase dos, en la instanciación de la plantilla.

Las reglas son un poco más complicadas, pero eso es lo esencial.

g depende del parámetro de plantilla T por lo que pasa la primera fase. Eso significa que si nunca crea una instancia de f , el código se compila bien. En la segunda fase f se crea una instancia con T = int . g(int) ahora se busca, pero no se encuentra:

17 : error: call to function ''g'' that is neither visible in the template definition nor found by argument-dependent lookup g(value); ^ 24 : note: in instantiation of function template specialization ''f<int>'' requested here f(2); ^ 20 : note: ''g'' should be declared prior to the call site void g(int v);

Para que un nombre arbitrario g pase con éxito, tenemos algunas opciones:

  1. Declarar g anteriormente:

void g(int); template <class T> void f(T value) { g(value); }

  1. trae g con T :

template <class T> void f(T) { T::g(); } struct X { static void g(); }; int main() { X x; f(x); }

  1. Traiga g con T través de ADL:

template <class T> void f(T value) { g(value); } struct X {}; void g(X); int main() { X x; f(x); }

Estos por supuesto cambian la semántica del programa. Están destinados a ilustrar lo que puede y no puede tener en una plantilla.

En cuanto a por qué ADL no encuentra g(int) , pero encuentra g(X) :

§ 3.4.2 Búsqueda de nombre dependiente del argumento [basic.lookup.argdep]

  1. Para cada tipo de argumento T en la llamada de función, hay un conjunto de cero o más espacios de nombres asociados y un conjunto de cero o más clases asociadas que deben considerarse [...]:

    • Si T es un tipo fundamental, sus conjuntos de espacios de nombres y clases asociados están vacíos.

    • Si T es un tipo de clase (incluidas las uniones), sus clases asociadas son: la clase en sí misma; la clase de la que es miembro, en su caso; y sus clases básicas directas e indirectas. Sus espacios de nombres asociados son los espacios de nombres de los cuales sus clases asociadas son miembros. [...]

Y finalmente llegamos a por qué extern void g(double); no se encuentra dentro de main: en primer lugar mostramos que g(fundamental_type) se encuentra iff se declara antes de la definición de f . Así que vamos a dejarlo void g(X) dentro de la main . ¿ADL lo encuentra?

template <class T> void f(T value) { g(value); } struct X{}; int main() { X x; void g(X); f(x); }

No. Debido a que no reside en el mismo espacio de nombres que X (es decir, espacio de nombres global), ADL no puede encontrarlo.

Prueba de que g no está en global

int main() { void g(X); X x; g(x); // OK ::g(x); // ERROR }

34: error: ningún miembro llamado ''g'' en el espacio de nombres global; ¿Querías decir simplemente ''g''?