Tengo que lidiar con una biblioteca que consta de muchas clases de plantilla, que son, por supuesto, todo implementado en los archivos de encabezado. Ahora estoy tratando de encontrar una manera de reducir el insoportablemente largos tiempos de compilación que provienen del hecho de que ya casi me tiene que incluir la totalidad de la biblioteca en todos y cada uno de mis unidades de compilación.

Es el uso de declaraciones forward una posibilidad, a pesar de las plantillas? Estoy tratando de hacer algo a lo largo de las líneas del ejemplo de abajo, donde he tratado de conseguir alrededor de la #include <vector>, como un ejemplo, pero me da un error del vinculador porque push_back es indefinido.

#include <iostream>

namespace std {
  template<class T>
  class vector {
  public:
    void push_back(const T& t);
  };
}

int main(int argc, char** argv) {
  std::vector<int>* vec = new std::vector<int>();
  vec->push_back(3);
  delete vec;
  return EXIT_SUCCESS;
}

$ g++ fwddecl.cpp
ccuqbCmp.o(.text+0x140): In function `main':
: undefined reference to `std::vector<int>::push_back(int const&)'
collect2: ld returned 1 exit status

Traté de encabezados precompilados una vez, pero eso no cambia los tiempos de compilación en todo (yo lo hice asegurarse de que, de hecho, fueron cargados en lugar de la verdadera encabezados). Pero si todos dicen que los encabezados precompilados debe ser el camino a seguir, a continuación, voy a dar que un intento de nuevo.

ACTUALIZACIÓN: Algunas personas dicen que no vale la pena de declarar la STL clases. Debo destacar que la STL vector anterior fue sólo un ejemplo. No estoy tratando de declarar clases STL, pero se trata de otros, fuertemente basados en plantillas clases de alguna librería que tengo que usar.

ACTUALIZACIÓN 2: hay una manera de hacer el ejemplo de arriba, en realidad compilar y enlazar correctamente? Logan sugiere la utilización de -fno-implicit-templates y poner template class std::vector<int> en algún lugar, presumiblemente en un .cpp archivo que se compila con -fno-implicit-templates, pero sigo teniendo errores del vinculador. De nuevo, estoy tratando de entender cómo funciona para std::vector para que luego se pueden aplicar a la plantilla de las clases que estoy usando.

  • en tu ejemplo, no ha de avanzar declarado nada. Todo lo que hice fue crear una plantilla de clase denominada vector en el espacio de nombres std. Entonces usted no pudo definir el push_back método declarado en la misma. De ahí el error del vinculador.
  • Segundo Evan explicación sobre el por qué de avance declarar std::vector<int> no funciona (te falta al menos un argumento en el interior de los soportes de ángulo). Trate de usar una plantilla de clase que se escribió a sí mismo, que usted sabe que no tiene ningún valor predeterminado argumentos de plantilla.
InformationsquelleAutor | 2009-02-17

4 Comentarios

  1. 35

    Usted no puede declarar hacia delante «partes» de las clases como las que. Incluso si usted puede, usted todavía necesita para ejecutar el código en algún lugar, así que usted podría enlace en contra de ella. Hay maneras de manejar, usted puede hacer usted mismo una pequeña biblioteca con las instancias de común contenedores (por ejemplo, vector) y el enlace en. Entonces quieres sólo se necesita compilar por ejemplo, vector<int> una vez. Para implementar esto, usted tendrá que usar algo como -fno-implicit-templates, al menos, suponiendo que se están pegando con g++ y explícitamente crear una instancia de la plantilla en su lib con template class std::vector<int>


    Así, un verdadero ejemplo de trabajo. Aquí tengo 2 archivos, a.cpp y b.cpp

    a.cpp:

    #include <vector> //still need to know the interface
    #include <cstdlib>
    
    int main(int argc, char **argv) {
      std::vector<int>* vec = new std::vector<int>();
      vec->push_back(3);
      delete vec;
      return EXIT_SUCCESS;
    }

    Así que ahora puedo compilar a.cpp con -fno-implicit-templates:

    g++ -fno-implicit-templates -c a.cpp

    Esto me dará una.o. Si yo trato de un enlace.o I get:

    g++ a.o
    /usr/bin/ld: Undefined symbols:
    std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&)
    void std::_Destroy<int*, std::allocator<int> >(int*, int*, std::allocator<int>)
    collect2: ld returned 1 exit status

    No es bueno. Así que nos dirigimos a b.cpp:

    #include <vector>
    template class std::vector<int>;
    template void std::_Destroy(int*,int*, std::allocator<int>);
    template void std::__uninitialized_fill_n_a(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&, std::allocator<int>);
    template void std::__uninitialized_fill_n_a(int*, unsigned long, int const&, std::allocator<int>);
    template void std::fill(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&);
    template __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::fill_n(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&);
    template int* std::fill_n(int*, unsigned long, int const&);
    template void std::_Destroy(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, std::allocator<int>);

    Ahora estás diciendo a ti mismo, ¿de dónde salieron todos estos extras de la plantilla de las cosas provienen de? Veo la template class std::vector<int> y eso está bien, pero ¿y el resto? Bueno, la respuesta corta es que, estas cosas implementaciones son, por necesidad, un poco desordenado, y cuando usted manualmente crear instancias de las mismas, por extensión, algunos de este desorden se filtra. Probablemente te estés preguntando cómo incluso me di cuenta de lo que se necesita para crear una instancia. También he utilizado el enlazador errores ;).

    Así que ahora tenemos que compilar b.cpp

    g++ -fno-implicit-templates -c b.cpp

    Y obtenemos b.o. La vinculación de una.o y b.o podemos obtener

    g++ a.o b.o

    ¡Hurra, no enlazador errores.

    Así, para entrar en algunos detalles sobre su actualizado pregunta, si esto es una elaboración casera de clase no necesariamente tiene que ser de esta desordenado. Por ejemplo, puede separar la interfaz de la aplicación, por ejemplo, dicen que tenemos c.h, c.cpp, además de a.cpp y b.cpp

    c.h

    template<typename T>
    class MyExample {
    T m_t;
    MyExample(const T& t);
    T get();
    void set(const T& t);
    };

    c.cpp

    template<typename T>
    MyExample<T>::MyExample(const T& t) : m_t(t) {}
    template<typename T>
    T MyExample<T>::get() { return m_t; }
    template<typename T>
    void MyExample<T>::set(const T& t) { m_t = t; }

    a.cpp

     #include "c.h" //only need interface
    #include <iostream>
    int main() {
    MyExample<int> x(10);
    std::cout << x.get() << std::endl;
    x.set( 9 );
    std::cout << x.get() << std::endl;
    return EXIT_SUCCESS;
    }

    b.cpp la «biblioteca»:

     #include "c.h" //need interface
    #include "c.cpp" //need implementation to actually instantiate it
    template class MyExample<int>;

    Ahora compilar b.cpp a b.o una vez. Cuando a.cpp los cambios sólo se necesita recompilar el que y el vínculo en b.o.

    • Excelente respuesta! Gracias por el paso a paso de caminar a través de 🙂
    • +1 Largo de la plantilla de los tiempos de construcción se me están volviendo loco! tyvm para el ejemplo detallado de cómo salir de la plantilla-construir-el infierno…
  2. 22

    Adelante declaraciones permiten hacer esto:

    template <class T> class vector;

    A continuación, usted puede declarar referencias y enlaces a vector<whatever> sin definir vector (sin incluir vector‘s archivo de encabezado). Esto funciona de la misma como el reenvío de las declaraciones de regular (sin plantilla) clases.

    El problema con las plantillas en particular, es que se necesita no sólo la declaración de la clase, sino también todas las definiciones de método en el archivo de encabezado (de modo que el compilador puede crear una instancia de la necesaria plantillas). Explícito ejecución de plantilla (de la que puede forzar el uso de con -fno-implicit-templates) es una solución para esto; usted puede poner su método de definiciones en un archivo de origen (o, siguiendo el ejemplo de la Google Guía De Estilo, en un -inl.h archivo de encabezado que usted no tiene que incluir) a continuación, explícitamente crear una instancia de la siguiente forma:

    template <class int> class vector;

    Tenga en cuenta que usted no necesita realmente -fno-implicit-templates para beneficiarse de esta; el compilador en silencio evitar crear instancias de las plantillas no tiene definiciones para, en el supuesto de que el vinculador figura más tarde. Y la adición de -fno-implicit-templates hará que el uso de todos plantillas más difícil (no sólo el tiempo que consume queridos), por lo que no la recomendaría.

    El problema con el código de ejemplo es que no estás adelante declarando el verdadero std::vector clase. Por no incluidos <vector>, usted puede crear su propio, no estándar vector clase, y no está nunca de definir push_back, así que no hay nada para el compilador para crear una instancia.

    He usado encabezados precompilados con gran efecto; no estoy seguro de por qué no le ayuda. Se ponen todos de no cambiar los encabezados en un solo all.h, precompilado, y verificado con strace o similares, que all.h.pch fue cargado individuales y archivos de encabezado no fueron? (Cómo usar strace: en lugar de ejecutar g++ mytest.cc, ejecutar strace -o strace.out g++ mytest.cc, a continuación, ver strace.out en un editor de texto y busque open( llamadas para ver los archivos que están siendo leídos.)

    • Un buen punto wrt -fno-implícita-de plantillas. Me despertó la distraído por la idea de «avance declarar» plantillas, cuando el problema es la velocidad de compilación.
  3. 6

    Con declaraciones forward sólo puede declarar miembros o parámetros como puntero o una referencia a ese tipo. Usted puede utilizar cualquiera de los métodos o de otras cosas que requieren las entrañas de dicho tipo. Que dije que había encontrado declaraciones forward realmente limita cuando se trata de acelerar los tiempos de compilación. Le sugiero que investigue la posibilidad de encabezados precompilados un poco más desde que descubrí que realmente ayuda con tiempos de compilación, a pesar de que estaba con el uso de Visual C++ en Windows y no de g++.

    • Si vas a usar cmake, el cotire plugin es increíble para tomar el cuidado de PCH’s para usted. github.com/sakra/cotire Es super fácil de usar y que «simplemente funciona»
  4. 3

    Hay <iosfwd> que le dará algunos declaración de las clases iostream, pero en general no hay mucho que usted puede hacer acerca de la stl, plantillas, en términos de avance de declararlos.

    Encabezados precompilados son el camino a seguir. Usted no notará ninguna velocidad de aumento de la primera vez que se compile, pero sólo se debe pagar el precio de una vez por cada vez que modifique el archivo de encabezado precompilado (o cualquier cosa incluida en ella).

    Ver a esta pregunta para otras ideas acerca de acelerar la compilación.

Dejar respuesta

Please enter your comment!
Please enter your name here