Come tutti i newbie del C++ si arriva prima o poi a chiedersi se sia possibile dichiarare la friendship e la forward declaration di un template. A quanto pare lo standard dice di si e da una veloce ricerca in rete la funzionalità risulta ampiamente supportata (sicuramente sia dal MSVC sia dal GCC).
Per la friendship si ha che:
L'output è il seguente:
friend class Foo;diventa, nel caso in cui Foo sia un template:
template<typename T> friend class Foo;
Nel caso delle forward declaration si piazza nell'header la dichiarazione del template da forward-declarare con la medesima sintassi che si usa normalmente per le classi templatiche.
Nell'esempio che segue viene utilizzata sia la friendship che la forward declaration "templatica": l'esempio in se non è assolutamente da prendere come modello di buona programmazione e ha il solo fine di mostrare la sintassi.
Si ha:
Nell'esempio che segue viene utilizzata sia la friendship che la forward declaration "templatica": l'esempio in se non è assolutamente da prendere come modello di buona programmazione e ha il solo fine di mostrare la sintassi.
Si ha:
- Un template Foo, con un metodo Print() che consente di stampare il valore intero di un membro privato contenuto in un oggetto di tipo Foo2. Giusto per dare un senso all'uso del template, il metodo Print stampa anche il valore di un builtin il cui tipo è dipendente dal parametro templatico. L'oggetto di tipo Foo2 è il builtin vengono passati a Foo nel costruttore.
- Una classe Foo2 che contiene un metodo PrintFoo(): questo prende in ingresso un oggetto di tipo Foo e ne invoca il metodo Print().
Un classico problema di dipendenza circolare risolvibile con forward declaration.
Innanzitutto dichiaro in un header la classe Foo:
Innanzitutto dichiaro in un header la classe Foo:
//foo.h #pragma once #include<iostream> #include "foo2.h" namespace FooNamespace { using namespace Foo2Namespace; template <typename T> class Foo { public: Foo(T toPrintObject, Foo2 foo2Object) : mToPrintObject(toPrintObject) , mFoo2Object(foo2Object) { //Nothing to do here } void Print() { std::cout << "Foo2 toPrintObject value = " << mToPrintObject << std::endl; std::cout << "Foo toPrint value = " << mFoo2Object.mToPrintValue << std::endl; } private: typename T mToPrintObject; Foo2 mFoo2Object; }; } //end header
Nell'header ho incluso l'header di Foo2. Notare che all'interno della classe Foo ho 2 membri, uno dipendente dal parametro templatico e uno di tipo Foo2. Quest'ultimo posso istanziarlo tranquillamente poichè, avendone incluso l'header, risulta un tipo completo di cui è conosciuto il memory layout.
L'header Foo2.h è invece definito come segue:
//foo2.h #pragma once //forward declaration namespace FooNamespace { template <typename T> class Foo; } namespace Foo2Namespace { class Foo2 { public: Foo2(int toPrintValue) : mTtoPrintValue(toPrintValue) { //Nothing to do here } template <typename T> void PrintFoo(Foo<T>& fooObject) { fooObject.Print(); } template <typename T> void PrintFoo(Foo<T>* fooObject) { fooObject->Print(); } private: //friendship template declaration template<typename T> friend class FooNamespace::Foo; int mToPrintValue; }; } //end header
In questo caso l'header di Foo non è stato incluso proprio al fine di evitare la dipendenza circolare. Ma poiché le funzioni e la friendship sono dipendenti da Foo, devo segnalare che Foo esiste. Per farlo è sufficiente dichiarare Foo prima che venga utilizzata da Foo2.
Dato che Foo esiste in un altro namespace (FooNamespace) occorre dichiararla all'interno del namespace stesso:
Dato che Foo esiste in un altro namespace (FooNamespace) occorre dichiararla all'interno del namespace stesso:
//forward declaration namespace FooNamespace { template <typename T> class Foo; }
Notare che nei 2 overload delle funzioni PrintFoo() si prende in ingresso una volta un puntatore e una volta un reference. Quando in una classe si usa un tipo che è stato forward declarato è possibile utilizzare solo puntatori e reference. Se avessi passato Foo<T> per copia il compilatore avrebbe segnalato un errore di tipo "incomplete type not allowed".
Il main è definito in questo modo:
#include "Foo.h" using namespace Foo2Namespace; using namespace FooNamespace; int main(int argc, char* argv[]) { Foo2 foo2(100); Foo<int> intFoo(10, foo2); Foo<float> floatFoo(10.5, foo2); std::cout << "----- intFoo print ----- \n\n"; foo2.PrintFoo(intFoo); std::cout << "\n----- floatFoo print -----\n\n"; foo2.PrintFoo(&floatFoo); getchar(); return 0; }
L'output è il seguente:
Nessun commento:
Posta un commento