Question

Je rencontre le problème suivant lors de l’instanciation de modèle [*].

fichier toto.h

class Foo
{
public:
    template <typename F>
    void func(F f)

private:
    int member_;
};

fichier foo.cc

template <typename F>
Foo::func(F f)
{
     f(member_);
}

fichier caller.cc

Foo::func(boost::bind(&Bar::bar_func, bar_instance, _1));

Bien que cela compile bien, l'éditeur de liens se plaint d'un symbole non défini:

void Foo::func<boost::_bi::bind_t...>

Comment puis-je instancier la fonction Foo::func? Puisqu'il prend une fonction comme argument, je suis un peu confus. J'ai essayé d'ajouter une fonction d'instanciation dans foo.cc , comme d'habitude avec les types non fonctionnels classiques:

instantiate()
{
    template<> void Foo::func<boost::function<void(int)> >(boost::function<void(int)>);
}

Évidemment, cela ne fonctionne pas. J'apprécierais que quelqu'un puisse me diriger dans la bonne direction.

Merci!

[*] Oui, j'ai lu la FAQ de parashift lite.

Était-ce utile?

La solution

La réponse à cette question dépend du compilateur. Certaines versions du compilateur Sun C ++ gèrent cela automatiquement en créant un cache d'implémentations de fonctions de modèle partagées entre plusieurs unités de traduction.

Si vous utilisez Visual C ++ et tout autre compilateur qui ne le peut pas, vous pouvez également placer la définition de la fonction dans l'en-tête.

Ne vous inquiétez pas des définitions en double si l'en-tête est inclus dans plusieurs fichiers .cc. Le compilateur marque les méthodes générées par les modèles avec un attribut spécial afin que l'éditeur de liens sache jeter les doublons au lieu de se plaindre. C'est l'une des raisons pour lesquelles C ++ a la & "Une règle de définition &";

.

Modifier: les commentaires ci-dessus s'appliquent au cas général où votre modèle doit être capable de lier des paramètres de type quelconques. Si vous connaissez un ensemble fermé de types utilisés par les clients, vous pouvez vous assurer qu'ils sont disponibles en utilisant une instanciation explicite dans le fichier d'implémentation du modèle, ce qui obligera le compilateur à générer des définitions pour les autres fichiers. Mais dans le cas général où votre modèle doit travailler avec des types éventuellement uniquement connus du client, il est alors inutile de séparer le modèle en un fichier d’en-tête et un fichier d’implémentation; de toute façon, tout client doit inclure les deux parties. Si vous souhaitez isoler les clients des dépendances complexes, masquez ces dépendances derrière des fonctions non basées sur un modèle, puis appelez-les à partir du code du modèle.

Autres conseils

Séparez-le en fichiers comme vous le souhaitez:
Ce n'est pas que je recommande ceci. Juste montrer que c'est possible.

plop.h

#include <iostream>
class Foo
{
public:
    Foo(): member_(15){}


    // Note No definition of this in a header file.
    // It is defined in plop.cpp and a single instantiation forced
    // Without actually using it.
    template <typename F>
    void func(F f);

private:
    int member_;
};


struct Bar
{
     void bar_func(int val) { std::cout << val << "\n"; }
};

struct Tar
{
    void tar_func(int val) { std::cout << "This should not print because of specialisation of func\n";}
};

Plop.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

template <typename F>
void Foo::func(F f)
{
     f(member_);
}

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Bar, int>, boost::_bi::list2<boost::_bi::value<Bar>, boost::arg<1> (*)()> > myFunc;

// Force the compiler to generate an instantiation of Foo::func()
template void Foo::func<myFunc>(myFunc f);

// Note this is not a specialization as that requires the <> after template.
// See main.cpp for an example of specialization.

main.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Tar, int>, boost::_bi::list2<boost::_bi::value<Tar>, boost::arg<1> (*)()> > myTar;

// Specialization of Foo::func()
template<> void Foo::func<myTar>(myTar f)
{
    std::cout << "Special\n";
}
// Note. This is not instantiated unless it is used.
// But because it is used in main() we get a version.

int main(int argc,char* argv[])
{
    Foo f;
    Bar b;
    Tar t;

    f.func(boost::bind(&Bar::bar_func, b, _1)); // Uses instantiation from plop.cpp
    f.func(boost::bind(&Tar::tar_func, t, _1)); // Uses local specialization
}

Incluez-vous foo.cc dans caller.cc. L'instanciation est quelque chose qui se produit au moment de la compilation - lorsque le compilateur voit l'appel dans l'appelant, il crée des versions instanciées des modèles, mais il doit disposer de la définition complète.

Je pense qu'ils font tous deux référence au fait que les définitions de fonction de modèle (et pas seulement les déclarations) doivent être incluses dans le fichier où elles sont utilisées. Les fonctions de modèle n'existent réellement que si / jusqu'à ce qu'elles soient utilisées; si vous les mettez dans un fichier cc séparé, le compilateur ne les connaît pas dans les autres fichiers cc, à moins que vous #include explicitement <=> ce fichier cc soit dans le fichier d'en-tête, soit dans le fichier qui les appelle, à cause du chemin l'analyseur fonctionne.

(C'est pourquoi les définitions de fonction de modèle sont généralement conservées dans les fichiers d'en-tête, comme l'a décrit Earwicker.)

Plus clair?

Je pense que Earwicker a raison. Le problème avec l'instanciation explicite de la fonction de membre de modèle func dans ce cas est que le type renvoyé par boost :: bind dépend de la mise en oeuvre. Ce n'est pas un boost :: function. Un boost :: function peut contenir un boost: bind, car il dispose d'un opérateur d'affectation de modèle qui en déduit le type du côté droit (le résultat de boost :: bind). Dans cette utilisation particulière de func dans caller.cc, avec cette implémentation particulière de boost, le type de boost :: bind est en fait le type mentionné dans l'erreur de l'éditeur de liens entre les & Lt; et > (ie. boost::_bi::bind_t...). Mais instancier explicitement func pour ce type aura probablement des problèmes de portabilité.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top