2010-01-20 12 views
8

¿Hay alguna diferencia entre definir funciones de miembro para una clase de plantilla dentro de la declaración de clase frente a afuera?Diferencia entre las funciones de miembro para una clase de plantilla definida dentro y fuera de la clase

definida en el interior:

template <typename T> 
class A 
{ 
public: 
    void method() 
    { 
     //... 
    } 
}; 

definida fuera:

template <typename T> 
class B 
{ 
public: 
    void method(); 
}; 

template <typename T> 
void B<T>::method() 
{ 
    //... 
} 

Para las clases no de plantilla, esta es la diferencia entre los métodos inline y no inline. ¿Esto también es cierto para las clases de plantilla?

El valor predeterminado para la mayoría de mis colegas es proporcionar definiciones dentro de la clase, pero siempre he preferido las definiciones fuera de la clase. ¿Está justificada mi preferencia?

Editar: Asuma que todo el código anterior se proporciona en el archivo de encabezado de la clase.

+0

Nunca he visto una referencia en ninguna parte que indique que la definición de un cuerpo de método dentro de la declaración de clase hace que ese método esté en línea. ¿Me he estado perdiendo algo? – Dathan

+0

@Dathan: Vea aquí: http://www.parashift.com/c++faq-lite/inline-functions.html#faq-9.8 Y aquí: http: //msdn.microsoft.com/en-us /library/bw1hbe6y%28VS.80%29.aspx – Ben

+3

@Dathan: Ha estado perdiendo §9.3/2 del estándar C++, que dice: "Una función miembro puede definirse (8.4) en su definición de clase, en la cual caso es una función miembro en línea ... "Editar: También tenga en cuenta que esta es una definición de clase - una declaración de clase es algo así como:' clase x; ' –

Respuesta

2

Sí, exactamente lo mismo es cierto para las clases de plantilla.

La razón por la cual las definiciones de métodos para las clases de plantilla generalmente se prefieren para estar en línea es que con las plantillas, toda la definición debe estar visible cuando se crea una instancia de la plantilla.

Así que si pones la definición de la función en un archivo .cpp separado, obtendrás un error del enlazador. La única solución general es hacer que la función entre en línea, ya sea definiéndola dentro de la clase o fuera de ella con la palabra clave inline. pero en ambos casos, debe ser visible en cualquier lugar donde se llame a la función, lo que significa que normalmente debe estar en el mismo encabezado que la definición de la clase.

+0

Solo por curiosidad, ¿el error del enlazador es universalmente verdadero ahora? Sé que en el pasado algunos Los compiladores proporcionaron diferentes opciones de instanciación de plantilla. El compilador SGI Irix CC, por ejemplo, predeterminado para creación de instancias de tiempo de enlace y parecía animar a poner definiciones de función de plantilla en su propio archivo .cpp (es decir, no en encabezado, no en línea, no visible al código de llamada, al igual que cualquier otra definición de función no en línea). Solo me pregunto si la definición de la función de la plantilla en línea ha cambiado de ser una preferencia del compilador a un requisito del idioma. – Darryl

+0

@Darryl, usted está describiendo plantillas externas. -la extensión estándar, y no todos los compiladores lo soportan. El nuevo estándar, C++ 1x, está agregando plantillas 'extern', aunque creo Creo que requiere la palabra clave. – greyfade

+0

Las plantillas se compilan en todas y cada una de las unidades de compilación que usan esa instanciación particular, pero son símbolos "débiles" (terminología gcc, no sé qué estándar) y tener el mismo símbolo definido en más de una unidad de compilación no ser un error de enlace Por lo tanto, no es necesario agregar 'inline' a las funciones/métodos de la plantilla. Otra observación es que 'inline' es una pista para el compilador, pero puede ser ignorado por el compilador (pero afectará cómo se crean los símbolos para que el enlazador no se queje de una definición doble). –

1

No hay diferencia, aparte de tener que escribir más. Eso incluye el bit template, inline y tener que usar nombres más "elaborados" cuando se refiere a la clase. Por ejemplo

template <typename T> class A { 
    A method(A a) { 
    // whatever 
    } 
}; 

template <typename T> inline A<T> A<T>::method(A a) { 
    // whatever 
} 

Tenga en cuenta que cuando se define la forma en el interior siempre se puede omitir la lista de parámetros de plantilla <T> cuando se hace referencia a A<T> y sólo tiene que utilizar A. Al definirlo afuera, debe usar el nombre "completo" en el tipo de devolución y en el nombre del método (pero no en la lista de parámetros).

+0

Está haciendo "método" en línea en ambos casos, lo que podría ser malo si el cuerpo de la función es grande. Eso es lo que trato de evitar. – Ben

+2

La palabra clave ** en línea ** tiene menos que ver con la optimización real del código, y más con si se permiten o no múltiples definiciones de la misma función.Si una función se define en un encabezado (lo cual es inevitable con las plantillas, o el caso cuando se define una función dentro de una declaración de clase), y múltiples unidades de compilación incluyen eso, el enlazador finalmente terminará con múltiples definiciones (idénticas) del misma función/método. La palabra clave ** inline ** (que debe estar implícita en las plantillas) le dice al vinculador que está bien, de lo contrario, sería un error del enlazador. – UncleBens

+0

El punto es que Ben con una plantilla no tiene otra opción. Tiene que marcarse en línea o la plantilla no funcionará. Esta es una razón por la que a algunas personas no les gustan las plantillas. También vale la pena señalar que el compilador en realidad no TIENE que alinear lo que puede hacer lo que quiera a sus espaldas. – Goz

0

Sé esto ... Creo que debe ser algo que ayuda a usted?

defining a member function outside of its template 

No es aceptable para proporcionar la definición de una función miembro de una clase de plantilla como esta:

// This might be in a header file: 
template <typename T> 
class xyz { 
    void foo(); 
    }; 

// ...

// This might be later on in the header file: 
    void xyz<T>::foo() { 
// generic definition of foo() 
    } 

esto está mal para una algunas razones Así es la siguiente:

 void xyz<class T>::foo() { 
     // generic definition of foo() 
     } 

La definición adecuada necesita la palabra clave de plantilla y los mismos argumentos de plantilla que la definición de la plantilla de clase se declaran con.Por lo que da:

 template <typename T> 
      void xyz<T>::foo() { 
      // generic definition of foo() 
       } 

Tenga en cuenta que existen otros tipos de instrucciones de la plantilla, como plantillas miembros, etc., y cada uno lleva en su propia forma. Lo importante es saber cuál tienes para saber cómo escribir cada sabor. Esto es así especialmente porque los mensajes de error de algunos compiladores pueden no ser claros en cuanto a qué está mal. Y, por supuesto, obtener un buen libro actualizado.

Si usted tiene una plantilla anidada dentro de un miembro de la plantilla:

template <typename T> 
     struct xyz { 
     // ... 
     template <typename U> 
     struct abc; 
     // ... 
     }; 

¿Cómo se define abc exterior de XYZ? Esto no funciona:

 template <typename U> 
    struct xyz::abc<U> { // Nope 
     // ... 
    }; 

ni hace esto:

template <typename T, typename U> 
struct xyz<T>::abc<U> { // Nope 
// ... 
}; 

Usted tendría que hacer esto:

 template <typename T> 
     template <typename U> 
      struct xyz<T>::abc { 
      // ... 
      }; 

Tenga en cuenta que es ...abc no ...abc<U> porque abc es una "primaria" modelo. IOWs, esto no es bueno:

// no permitido aquí: plantilla de plantilla struct xyz :: abc {};

Cuestiones relacionadas