Frage

Ich habe folgendes Problem mit Vorlage Instanziierung [*].

Datei foo.h

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

private:
    int member_;
};

Datei foo.cc

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

Datei caller.cc

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

Während diese kompiliert fein, der Linker beschwert mich über ein undefiniertes Symbol:

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

Wie kann ich instanziiert die Funktion Foo::func? Da es sich um eine Funktion als Argument nimmt, bin ich etwas verwirrt. Ich habe versucht, eine Instanziierung Funktion in foo.cc zu schreiben, ich bin es gewohnt, mit regelmäßiger nicht-Funktion Typen:

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

Natürlich funktioniert das nicht. Ich würde mich freuen, wenn mir jemand in die richtige Richtung zeigen kann.

Danke!

[*] Ja, ich lese die parashift FAQ lite.

War es hilfreich?

Lösung

Die Antwort auf diese ist Compiler abhängig. Einige Versionen der Sun C ++ Compiler würde behandeln dies automatisch durch einen Cache von Template-Funktion Implementierungen aufzubauen, die auf getrennte Übersetzungseinheiten geteilt werden würde.

Wenn Sie Visual C ++ verwenden, und andere Compiler, dass dies nicht möglich ist, können Sie auch die Funktionsdefinition im Header setzen können.

Keine Sorge über doppelte Definitionen, wenn der Header von mehreren .cc-Dateien enthalten ist. Der Compiler markiert Vorlage gestützter Methoden mit einem speziellen Attribut so der Linker Duplikate weiß statt beschweren wegzuwerfen. Dies ist ein Grund, warum C ++ hat die „eine Definition-Regel“.

Edit: Die obigen Ausführungen im allgemeinen Fall gelten, wenn die Vorlage keine Art der Verlinkung Parameter gegeben fähig sein müssen. Wenn Sie eine geschlossene Gruppe von Typen kennen, die Kunden verwenden, können Sie sicherstellen, dass sie unter Verwendung von expliziter Instanziierung in der Vorlage Implementierungsdatei zur Verfügung stehen, die bewirkt, dass die Compiler-Definitionen erzeugen, um andere Dateien zu verknüpfen gegen. Aber im allgemeinen Fall, in dem die Vorlage muss möglicherweise mit Typen arbeitete nur den Kunden bekannt, dann gibt es wenig Sinn, die Vorlage in eine Header-Datei zu trennen und und Implementierungsdatei; sowieso jeder Kunde braucht beiden Teile aufzunehmen. Wenn Sie möchten, dass Clients von komplexen Abhängigkeiten isolieren, diese Abhängigkeiten hinter Nicht-Template-Funktionen verstecken und dann in sie aus dem Template-Code aufrufen.

Andere Tipps

Splitting es in Dateien wie Sie wollen:
Nicht, dass ich empfehlen dies. Nur zeigen, dass es möglich ist.

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
}

Sind Sie mit foo.cc in caller.cc. Instanziierung etwas ist, das bei der Kompilierung geschieht -., Wenn der Compiler den Anruf in Anrufer sieht macht es instanzierte Versionen der Vorlagen muss aber verfügbar, um die vollständige Definition haben

Ich denke, was sie sich beziehen beide ist, dass die Template-Funktionsdefinitionen (nicht nur Erklärungen) in die Datei aufgenommen werden müssen, wo sie verwendet werden. Template-Funktionen gibt es nicht wirklich, es sei denn / bis sie verwendet werden; wenn man sie in einer separaten cc-Datei setzen, dann wird der Compiler nicht weiß, um sie in den anderen cc-Dateien, sofern Sie nicht ausdrücklich, dass cc-Datei #include entweder in die Header-Datei oder der Datei, die sie auf Grund der Art und Weise der Parser ruft funktioniert.

(Deshalb Definitionen Template-Funktion im Allgemeinen in den Header-Dateien gehalten werden, wie Earwicker beschrieben.)

Jedes klarer?

Ich glaube, Earwicker korrekt ist. Das Problem mit dem explizit der Vorlage Member-Funktion func in diesem Fall instanziieren ist, dass der Typ zurückgegeben von boost :: bind ist die Umsetzung abhängig. Es ist nicht ein boost :: Funktion. Eine boost :: Funktion kann enthalten einen Schub: bind, weil es eine Vorlage Zuweisungsoperator hat, die den Typ der rechten Seite (die boost :: bind Ergebnis) folgert. In diesem speziellen Einsatz von func in caller.cc, mit dieser speziellen Implementierung von Boost, die Art der boost :: bind ist eigentlich sie in den Linker-Fehlern genannte Art zwischen dem (dh. boost::_bi::bind_t...). Aber ausdrücklich func wahrscheinlich für diese Art der Instanziierung wird Portabilität Probleme haben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top