2010-11-30 8 views
7

Tengo una clase Foo que se usa en un pequeño proyecto independiente. Tiene una definición de clase en Foo.h con la implementación para las funciones miembro de la clase en un archivo de implementación Foo.cpp.¿Dónde definir la función de plantilla de miembro de la clase C++ y los funtores que la crean?

Primera pregunta: una de las funciones miembro de la clase Foo es un método de plantilla Foo :: doSomething(), ¿es correcto que la implementación de este método aparezca con la declaración de la función en Foo.h?

El parámetro de plantilla con el que se creará la instancia de Foo :: doSomething() es uno de los dos tipos de Functor - clase CalcA y CalcB.

¿Debo:

  • (A) poner el Defintion y la aplicación de las dos clases Functor todos juntos en foo.cpp (cuando hayan sido realmente utilizados por la aplicación de otras funciones miembro Foo Foo para llamar: :hacer algo).
  • (B) ponga la definición y la implementación de las dos clases de Functor en Foo.h.
  • (C) ¿Debería dividir la definición y la implementación de los dos Functors en Foo.h y Foo.cpp como se haría con una clase ordinaria?

Respuesta

7

Regla general:

Si foo :: doSomething() se utiliza fuera foo.cpp (es decir, si es público o protegido, por lo general), que debe ir en la cabecera.

Si no, poner en el archivo cpp está perfectamente bien, e incluso una buena idea (ya que mantiene el desorden lejos del archivo de encabezado).

Por lo tanto, si los funtores solo se usan en el archivo cpp, de todas formas ponga la función de plantilla allí también. Uno siempre puede refactorizar las cosas más adelante si esto cambia.

+1

En el caso en que se necesita una plantilla en un solo archivo CPP, entonces pondría la declaración y la definición solo en ese archivo. Si es una plantilla de función miembro y el resto de la clase se usa en otras unidades de traducción, definitivamente pondré la definición en el archivo .h. Incluso me preocuparía el comportamiento indefinido o definido por la implementación si esto no se hiciera. –

+0

@Marcus, debería haber mencionado que en la pregunta original - sí Foo :: doSomething() es una función de miembro de plantilla privada de la clase Foo. –

+1

@John, no sigo tu punto, ¿podrías elaborar un poco? En mi caso, tengo una función de miembro de plantilla privada de la clase Foo, por lo tanto, solo la usa la implementación de la clase Foo en Foo.cpp. Entonces eso parece encajar con el primer caso/oración en tu comentario. Pero mi situación también se ajusta a la descripción en la segunda oración de tu comentario ("plantilla de función de miembro y el resto de la clase se usa en otras unidades de traducción") donde dices que la pongas en un archivo .h. –

1

Mi defecto sería poner la definición de las plantillas de función miembro de la derecha en el archivo .h, así:

class Foo 
{ 
public: 
    template<typename T> void DoSomething(T t); 
}; 

// ... later... 

template<typename T> 
void Foo::DoSomething(T t) 
{ 
    // ... 
} 

Si este no es el óptimo para un caso en particular, entonces me gustaría tener medidas más heroicas. Comenzando con #include -ing un archivo .inc con la definición al final del archivo .h, o posiblemente incluso haciendo instancias explícitas en los archivos .cpp donde necesitaba las plantillas de funciones miembro que se utilizarán.

+0

En mi humilde opinión, se está sobrediseñando un poco, si es solo una función. Pero de todos modos es un tema de bikeshed. :) – Macke

+0

@Marcus: ¿Qué es específicamente overdesigning? –

+0

@Marcus: Creo que veo lo que está diciendo ahora –

1

La definición del método de la plantilla debería estar en el archivo de encabezado de la clase a la que pertenece.

De esta manera:

class MyClass 
{ 
    template <typename T> 
    void foo(const T&) 
    { 
     // Definition 
    } 
}; 

O como este (Tenga en cuenta que la definición del método de plantilla se pueden incluir desde un archivo separado después de la declaración de la clase)

class MyClass 
{ 
    template <typename T> void foo(const T&); 
}; 

template <typename T> 
void MyClass::foo(const T&) 
{ 
    // Definition 
} 

El resto es depende del estilo que de acuerdo y sus necesidades.

Pondré la declaración de functor (o incluso la definición si son simples) en el encabezado si las uso no solo en Foo o si Foo las tiene como miembro de clase.

7

Primero debes entender el mecanismo de las plantillas.Las plantillas no se compilan, se crean instancias cuando se usan y luego se compila su instanciación. Por lo tanto, el compilador necesita tener la definición de plantilla completa en cada módulo utilizando la función de plantilla, para crear una instancia primero de acuerdo con los parámetros que haya pasado.

Para resolver su problema, hay tres soluciones, pero verá que ambas conducen al mismo resultado. O te enteras aplicar sus plantillas en el archivo de cabecera dentro de la definición de clase (que utilizar al sufijo con .hxx en lugar de .h con el fin de precisos que están las definiciones que contiene plantillas):

// Foo.hxx 

#ifndef __FOO_HXX__ 
#define __FOO_HXX__ 

class Foo { 
    public: 
    template <class T>  
    void bar(const T& t) { 
     t.doSomething(); 
    } 
}; 
#endif 

o puede hacer externalizar la definición de la clase, pero todavía en el archivo de cabecera:

// Foo.hxx 

#ifndef __FOO_HXX__ 
#define __FOO_HXX__ 

class Foo { 
    public: 
     template <class T>  
     void bar(const T&); 
}; 

template <class T> 
void Foo::bar(const T& t) { 
    t.doSomething(); 
} 
#endif 

por último, se puede implementar métodos cuerpos plantilla en un archivo externo (con el prefijo .cxx por la misma razón). Contendrá cuerpos de métodos pero no incluirá "Foo.hxx". En cambio, es "Foo.hxx" que incluirá "Foo.cxx" después de la definición de la clase. De esta manera, cuando el compilador resuelve la directiva #include, encuentra toda la definición de plantilla en el mismo módulo, lo que le permite crear una instancia que:

// Foo.hxx 

#ifndef __FOO_HXX__ 
#define __FOO_HXX__ 
class Foo { 
    public: 
     template <class T>  
     void bar(const T&); 
}; 

#include "Foo.cxx" 

#endif 

// Foo.cxx 
template <class T> 
void Foo::bar(const T& t) { 
    t.doSomething(); 
} 

La elección entre estas 3 formas de implementar plantillas es más bien una cuestión de legibilidad (y gusto).
Segundo y tercero son equivalentes en términos de código generado, pero prefiero no usar la solución de archivo cxx, porque a menudo conduce a errores estúpidos cuando se olvida de invertir el include.

Además, las conocidas librerías C++ como STL o Boost proponen su código solo en archivos de encabezado, lo que es un signo de buen diseño. Al usar la definición externa dentro de los encabezados, aclaras la definición de tu clase. También evita que el compilador ingrese automáticamente los métodos, lo que a veces puede generar resultados deficientes según Herb Sutter http://www.gotw.ca/gotw/033.htm

+0

gracias por la respuesta. Debería haber mencionado en mi pregunta original que la función de miembro de plantilla en mi situación es una función de miembro privado de la clase Foo. Aparte de eso, dadas las tres sugerencias en su respuesta, ¿cuáles son las ventajas/desventajas de cada una? La principal diferencia que puedo ver es que en su primera sugerencia, la función de miembro estaría implícitamente en línea y en los dos siguientes no, ¿es correcto? ¿Qué ventajas/desventajas tienen el segundo y el tercero sobre el otro? –

+1

@Czarak: tienes razón -> métodos definidos en un cuerpo de la clase aparecen automáticamente (ver http://stackoverflow.com/questions/1443982/when-do-compilers-inline-c-code para más detalles acerca de procesos en línea) . Edité mi respuesta para agregar las precisiones que me pediste. Espero que te haya ayudado. – jopasserat

Cuestiones relacionadas