programa otro lenguaje incluir headers ejemplos codigo cabeceras cabecera archivos archivo c++ templates codeblocks header-files

c++ - otro - "Definición múltiple" al usar archivos de encabezado(simulados) para plantillas



incluir header en c (3)

En general, debe compilar el archivo .cpp por separado y vincularlo más tarde. Pero si quiere (o debe) hacerlo de esta manera, marque todas las funciones que no sean de plantilla como en inline . Esto debería ayudar.

Soy consciente de que las definiciones de las funciones de plantilla de C ++ deben colocarse en los archivos de encabezado. Sin embargo, por razones de legibilidad mejorada y estructura de una biblioteca (potencialmente) grande que estoy creando, separé las declaraciones de las implementaciones, en encabezados "simulados" (que #include los archivos de implementación, bastante parecido a esta estructura de archivos). Tenga en cuenta que soy consciente de que la implementación de las funciones con plantillas debe incluirse en tiempo de compilación, y lo estoy haciendo .

En resumen, tengo un error de "definición múltiple" cuando agrego una declaración de función sin plantilla en el archivo de implementación . Larga explicación con ejemplos a continuación.

Cuando el par de encabezados "simulados" + archivos de implementación solo contienen el par de declaración / implementación de la función de plantilla , todo funciona bien. También funciona bien cuando agrego una implementación de una nueva función de plantilla solo en el archivo de implementación .

Ejemplo de trabajo (incluiría #include "algo.h" en mi main.cpp cuando quisiera usar esta funcionalidad):

Archivo de cabecera " simulado " algo.h :

#ifndef ALGO_H #define ALGO_H namespace fl{ template <typename Compare> void algo(.. non-templated args .., Compare order = std::less<int>()); } #include "tpp/algo.cpp" #endif // ALGO_H

Archivo de implementación tpp / algo.cpp : (actualmente solo algo.tpp )
Nota: Usando el archivo tpp/.cpp estaba en la versión inicial, ahora estoy usando un archivo .tpp por sugerencia de @ πάντα ῥεῖ , al final.

#ifndef TPP_ALGO #define TPP_ALGO #include "../algo.h" namespace fl{ template <typename Compare> void subFunctionality(Compare order, .. args ..){ /* impl */ } template <typename Compare> void algo(.. non-templated args .., Compare order){ subFunctionality(order, .. args ..); // implementation } } #endif // TPP_ALGO

El problema surge cuando agrego una implementación de funciones sin plantillas en el archivo de implementación . Ejemplo (no funcional) de tpp / algo.cpp (actualmente solo algo.tpp ) (utilizando el mismo algoritmo.h ):

#ifndef TPP_ALGO #define TPP_ALGO #include "../algo.h" namespace fl{ template <typename Compare> void subFunctionality(Compare order, .. args ..){ /* impl */ } void moreSubFun(.. args ..) { /* impl */ } template <typename Compare> void algo( .. non-templated args ..., Compare order){ subFunctionality(order, .. args ..); moreSubFun(.. args ..); // more stuff } } #endif // TPP_ALGO

Obtengo el error de "definición múltiple" (desde donde lo main.cpp en main.cpp ), así:

obj/Release/main.o In function `fl::moreSubFun(...)'': main.cpp multiple definitions of `fl::moreSubFun(..)'' obj/Release/../tpp/algo.o:algo.cpp first defined here

¿Por qué sucede esto solo con las funciones sin plantillas, mientras que funciona bien para las plantillas , y más importante aún, cómo resuelvo este problema?

Miré alrededor, y no encuentro nada útil :( Idealmente, estoy buscando algo lo más cercano posible a mi estructura de archivos (pero tomaré todo lo que funcione mientras uso una separación en "simulación"). " .h + tpp/.cpp ). ¿Tengo que eliminar las subfuncionalidades adicionales en un par separado, no .h/.cpp archivos .h/.cpp , o hay otras soluciones? (Las subfuncionalidades deberían idealmente no ser visible para el usuario final).

Soy reacio a usar en inline al definir fl::moreSubFunc(..) ya que la función es bastante grande (y me enseñaron en inline lo ideal sería que solo se utilizara con funciones pequeñas). Esto resuelve el problema, pero estoy buscando si hay una solución diferente.

Estoy trabajando en Code::Blocks , usando gcc version 4.7.2 . Esta es la razón inicial por la que mi archivo de implementación es tpp/.cpp (extensión .cpp ), ya que Code::Blocks no lo admite de manera predeterminada. Esto se modifica en la implementación actual siguiendo la sugerencia de @ πάντα ῥεῖ (consulte más abajo).

La edición tardía (después de haber enseñado que se encontró la solución) enseñé que la respuesta de @ πάντα ῥεῖ resuelve el problema. Modifiqué Code::Blocks para aceptar archivos .tpp (ya sea tratándolos como archivos de encabezado o fuente ). Inicialmente, esta solución funcionó.

Sin embargo, esta solución funcionó solo cuando el archivo algo.h se incluyó en solo otro archivo : cuando lo incluí solo en main.cpp . Sin embargo, tan pronto como traté de incluirlo en otro archivo fuente (por ejemplo, algo2.cpp ) que usaría esos algoritmos (además de main.cpp ), volvió el problema de la definición múltiple .

En pocas palabras , el problema persiste tan pronto como incluyo el algo.h en más de un archivo , y todavía estoy buscando una solución .


Como una buena regla general:

"Nunca incluya un archivo .cpp ".

No debe dar a estos archivos de implementación de plantilla una extensión .cpp . Su sistema de compilación podría incluirlos automáticamente en ese momento.

Las extensiones usadas convencionalmente para tales archivos son, por ejemplo, .tcc o .icc . Añádalos a su proyecto, ya que agregaría otros archivos de encabezado.
No los incluya en el proyecto como unidades de traducción compiladas y vinculadas por separado, si se usan con una instrucción #include de otro archivo de encabezado.

ACTUALIZAR:
Como pedía específicamente Bloques de código, solo necesita ajustar un poco la configuración de la extensión de archivo para que estos archivos se incluyan en su proyecto correctamente y tener la sintaxis coloreada de la forma habitual:

1. Agregue el nuevo tipo de archivo a la configuración de la extensión de archivo

2. Agregue el tipo de archivo a la configuración de la extensión de archivo del proyecto Project->Project Tree->Edit file types & categories

Tan pronto se .tcc un archivo .tcc al proyecto ahora, se abrirá usando el editor de texto y la sintaxis se coloreará de la forma habitual:

El archivo .hpp correspondiente se ve así


Su problema ocurre porque las plantillas de funciones se tratan de manera diferente en el tiempo de enlace de las funciones gratuitas "simples". Las funciones deben obedecer a la Regla de una sola definición (ODR) ; es decir, deben definirse en no más de una unidad de traducción. De lo contrario, cuando llega el momento del enlace, termina con múltiples errores de definición como el que citó. Esta misma regla también se aplica a clases, tipos y objetos en general.

Esto parecería excluir el uso de plantillas en absoluto; deben estar completamente incluidos en cada unidad de traducción en la que se utilizan. Sin embargo, la ODR hace una excepción en algunos casos. Citando de Wikipedia:

Algunas cosas, como tipos, plantillas y funciones en línea externas, se pueden definir en más de una unidad de traducción. Para una entidad dada, cada definición debe ser la misma. Los objetos y funciones no externos en diferentes unidades de traducción son entidades diferentes, incluso si sus nombres y tipos son los mismos.

Es por eso que no se encuentra con múltiples errores de definición con las funciones de la plantilla. En el momento del enlace, el vinculador encuentra las definiciones de símbolos duplicados y elimina todos los duplicados (siempre que sean equivalentes, de lo contrario, esto sería un error). Por lo tanto, su programa se vincula exitosamente con exactamente una definición de cada símbolo requerido.

Para su caso, su problema ocurre porque está incluyendo funciones que no son de plantilla en más de una unidad de traducción (en todas partes donde se .cpp archivo .cpp ). Habría algunas formas de solucionar esto:

  1. Si las funciones de plantilla son parte de una clase, puede mover las funciones que no son de plantilla para que también se encuentren en esa clase. Esto lo colocaría bajo el paraguas de desduplicación de símbolos de la clase de plantilla propietaria.

  2. Marque las funciones como en línea.

  3. Divida las funciones que no son de plantilla en otro archivo .cpp que luego compila por separado. Esa será la única unidad de traducción que los alojará.