Domanda

Ho il seguente problema utilizzando l'istanza del modello [*].

file foo.h

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

private:
    int member_;
};

file foo.cc

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

file caller.cc

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

Mentre questo compila bene, il linker si lamenta di un simbolo indefinito:

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

Come posso creare un'istanza della funzione Foo::func? Dal momento che prende una funzione come argomento, sono un po 'confuso. Ho provato ad aggiungere una funzione di istanza in foo.cc , come sono abituato con i normali tipi non funzionali :

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

Ovviamente, questo non funziona. Gradirei se qualcuno potesse indicarmi la giusta direzione.

Grazie!

[*] Sì, ho letto le FAQ del parashift lite.

È stato utile?

Soluzione

La risposta è dipendente dal compilatore. Alcune versioni del compilatore Sun C ++ lo gestiranno automaticamente creando una cache di implementazioni di funzioni modello che sarebbero condivise tra unità di traduzione separate.

Se stai usando Visual C ++ e qualsiasi altro compilatore che non può farlo, puoi anche inserire la definizione della funzione nell'intestazione.

Non preoccuparti delle definizioni duplicate se l'intestazione è inclusa da più file .cc. Il compilatore contrassegna i metodi generati dal modello con un attributo speciale in modo che il linker sappia eliminare i duplicati invece di lamentarsi. Questo è uno dei motivi per cui C ++ ha la & Quot; una regola di definizione & Quot ;.

Modifica: I commenti sopra riportati si applicano nel caso generale in cui il modello deve essere in grado di collegare dati determinati parametri di tipo. Se conosci un insieme chiuso di tipi che i client useranno, puoi assicurarti che siano disponibili usando l'istanza esplicita nel file di implementazione del modello, che farà sì che il compilatore generi definizioni per altri file da collegare. Ma nel caso generale in cui il tuo modello deve funzionare con tipi probabilmente conosciuti solo dal client, allora ha poco senso separare il modello in un file di intestazione e in un file di implementazione; ogni cliente deve comunque includere entrambe le parti. Se si desidera isolare i client da dipendenze complesse, nascondere tali dipendenze dietro funzioni non basate su modelli e quindi richiamarle dal codice del modello.

Altri suggerimenti

Dividendolo in file Come vuoi:
Non che lo consiglio. Sto solo dimostrando che è possibile.

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
}

Stai includendo foo.cc in caller.cc. L'istanza è qualcosa che accade al momento della compilazione: quando il compilatore vede la chiamata nel chiamante crea versioni istanziate dei modelli ma deve avere la definizione completa disponibile.

Penso che entrambi facciano riferimento al fatto che le definizioni delle funzioni del modello (non solo le dichiarazioni) debbano essere incluse nel file in cui vengono utilizzate. Le funzioni del modello in realtà non esistono a meno che / fino a quando non vengono utilizzate; se li metti in un file cc separato, il compilatore non li conosce negli altri file cc, a meno che tu non esplicitamente #include quel file cc nel file di intestazione o nel file che li chiama, a causa del modo il parser funziona.

(Ecco perché le definizioni delle funzioni del modello sono generalmente contenute nei file di intestazione, come descritto da Earwicker.)

Qualche chiarimento?

Credo che Earwicker abbia ragione. Il problema con l'istanza esplicita della funzione del membro modello in questo caso è che il tipo restituito da boost :: bind dipende dall'implementazione. È non una funzione boost ::. Una funzione boost :: può contenere un boost: bind perché ha un operatore di assegnazione di template che deduce il tipo del lato destro (il risultato boost :: bind). In questo particolare uso di func in caller.cc, con questa particolare implementazione di boost, il tipo di boost :: bind è in realtà il tipo menzionato nell'errore linker tra & Lt; e > (ad es. boost::_bi::bind_t...). Ma un'istanza esplicita di funzioni per quel tipo probabilmente avrà problemi di portabilità.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top