6
votes

Qu'est-ce qu'une manière robuste de spécialisation de modèle en C ++ pour en-tête / source séparée

Dans des projets modérés ou même de grandes projets complexes, la déclaration de modèle et la définition de modèle est utile réduire le temps de compilation.

Cependant, dans un code complexe, de petites erreurs de programmeurs peuvent entraîner un changement de comportement inaperçu, par exemple. Une version générique est appelée au lieu d'une spécialisation. P>

exemple: La spécialisation des modèles est devenue invisible en raison d'une déclaration manquée. P>

///////////////////// file  A.hpp /////////////////////

#include <iostream>

template <typename T>

class A 
{
public:
  void foo() 
  {
      std::cerr << " calling generic foo " << std::endl ;
  }
};

// forgetting following declaration leads to an unintended program behaviour
template <> void A< int >::foo();

///////////////////// file  A-foo-int.cpp /////////////////////
#include "A.hpp"

template <> 
void A< int >::foo()
{
  std::cerr << "calling  <int> version of foo" << std::endl;
}

///////////////////// file  main.cpp /////////////////////
#include "A.hpp"

int main(int argc , char** argv)
{
  A<int>* a = new A<int>();
  a->foo();
  return 0;
}

///////////////////// Makefile /////////////////////
CC = g++
CPPFLAGS += -Wall -O3
CXXFLAGS += --std=gnu++0x

all: nonrobust-template-setup

nonrobust-template-setup: main.o A-foo-int.o  
    $(CC)  $(CPPFLAGS) main.o A-foo-int.o  -o nonrobust-template-setup

clean: 
    rm -rf *.o nonrobust-template-setup

//////////////////////////////////////////


1 commentaires

Je pense que vous faites tout comme vous le devriez. C'est juste comment ça marche dans c ++ :)


4 Réponses :


2
votes

Vous ne pouvez pas séparer les déclarations et définitions de cette façon: si vous relégez la définition de vos fonctions de membre spécialisées dans un fichier .cpp distinct, peu importe si vous déclarez votre spécialisation immédiatement après le modèle principal, le compilateur ne pourra pas l'installer, et le lien de liaison se plaint des références non résolues.

Normalement, la définition des fonctions des membres d'un modèle de classe se déroule dans un fichier d'en-tête, sauf si vous fournissez un instanciation pour les modèles de classe correspondants: xxx

en général, sauf si vous êtes confronté à vraiment des problèmes horribles horribles délais de compilation horrible, Je vous suggère de suivre la pratique courante et de tout mettre dans un fichier d'en-tête à inclure par toutes les unités de traduction nécessaires à utiliser le modèle de classe: xxx

si vous êtes face à des problèmes de temps de compilation vraiment horribles, vous pouvez alors séparer les définitions de la fonction membre et les mettre en unités de traduction distinctes. Avec des instanciations explicites, mais en C ++ 11, il n'y a pas de moyen propre / simple pour vous assurer que toutes les spécialisations que vous relégez dans des fichiers distincts .cpp sont déclarés immédiatement après le modèle principal (comme une bonne pratique recommande ). S'il y avait, je suppose que ce serait si populaire que vous n'auriez pas besoin de venir ici et de demander à ce sujet, car tout le monde fait face à un tel problème de conception.

Dans certains cas, certaines macros fantaisistes pourraient aider, mais ils apporteraient douteusement plus d'avantages que la douleur à l'entretien dans des projets vraiment complexes.

Une solution à ce problème a été tentée dans le C ++. 03 Standard en introduisant le mot-clé exportation , mais l'expérience de la mise en œuvre s'est révélée trop difficile à prendre en charge pour les fournisseurs de compilateur, c'est pourquoi exporter n'est plus une partie de la norme C ++ (puisque c ++ 11).

Espérons-le, une meilleure solution pour modules va entrer en C ++ 14 et fournir une solution à la conception de modèles.


2 commentaires

OK, il semble qu'il n'y ait aucune bonne solution pour ce problème encore disponible, à l'exception des tests d'écriture pour vérifier si une version de méthode correcte est appelée.


@Jarobkroeker: Oui, essentiellement, c'est l'impression que j'ai essayé de transmettre. Je crois qu'il n'y a pas encore de bonne solution pour ce problème.



1
votes

Je pense que le mieux que vous puissiez faire est de static_assert que le modèle générique n'est jamais instancié avec les types censés être spécialisés.

Le code suivant est d'illustrer uniquement - je ' d Probablement utiliser boost_static_assert (et std :: is_same si je pouvais utiliser c ++ 11). L'idée de base est d'empêcher d'instancier implicitement le modèle non spécialisé avec l'ensemble de types que vous interdisez. Bien sûr, si vous oubliez d'ajouter l'affirmation statique et la spécialisation que vous allez toujours échouer. xxx


2 commentaires

Si j'ai bien compris la question, l'OP souhaite veiller à ne pas oublier, immédiatement après le modèle principal, de déclarer une spécialisation explicite qu'il définit ultérieurement. Comment un static_assert aide dans ce cas?


ok, dans un petit projet, on pourrait probablement faire ce qui suit: Modèle Classe A {Public: Void Foo () {static_assert (non std :: is_same :: Valeur, "Spécialisation INT pour foo () n'est pas instancié! "); }}; mais cela est pénible dans un projet de taille moyenne



0
votes

Le moyen d'être sûr de cela est de ne pas fournir de définition du modèle FOO () code>. Il n'est pas nécessaire de déclarer des spécialisations lorsque vous le faites de cette façon:

// A.cc
#include <cstdio>
#include "A.h"
template<> void A<int>::foo() { puts("foo!"); }


3 commentaires

Dans plusieurs cas, une implémentation générique est nécessaire et utile, elle ne peut donc pas être supprimée.


Votre code fourni "Erreur: appelé Generic FOO" pour la mise en œuvre générique m'a amené à croire autrement, il serait peut-être utile de clarifier le libellé de questions pour d'autres


Re: votre code fourni "Erreur: appelé Generic FOO" pour la mise en œuvre générique m'a amené à croire autrement - oui, c'est vrai, désolé pour cela. Peut-être que je devrais éditer l'exemple de code maintenant (si cela est possible)



0
votes

ok, des commentaires instanciant de la mise en œuvre générique A :: foo () n'est pas nécessairement une erreur, ce n'est que si vous avez fourni une spécialisation ailleurs.

Alors ce que vous voulez Pour trouver des instanciations de gabarit génériques dont les noms de spécialisation en double qui ne devraient avoir été instanciés que dans une liste spécifique des fichiers d'objet compilateur - qui réduit à la recherche de champs de correspondance dans deux jeux de données. Pour cela, il y a rejoindre : xxx

EDIT: SED Syntaxe pour les modèles Grep n'a pas vraiment fonctionné très bien.


0 commentaires