[C++] Szablon funkcji + klasa szablonowa

0

Witam.
Jestem w trakcie poznawania tajników C++.
No i mam takie pytanie. Mam jakąś klasę szablonową która zawiera parametr inny niż typ - czyli załóżmy że liczbę całkowitą (int).

template <int LICZBA> class Test {};

W szablonie tym deklaruję sobie przyjaźń z jakąś globalną funkcją szablonową, która jak wiemy może mieć jako parametr tylko typ. Niech będzie to:

template <int LICZBA> class Test 
{
    public:
        void print() { 
             cout << LICZBA; 
        }
    friend void funkcja (T obiekt, Test<LICZBA> tmp);
}

Mam teraz deklarację takiej funkcji:

template <class t> void funkcja (t & obiekt) { obiekt.print(); } 

No i do tej pory jest wszystko ok... Problem zaczyna się w momencie gdy jako funkcję zaprzyjaźnioną zapragnę mieć np. operator....

friend ostream& operator<< (ostream& out, Test<LICZBA>& tmp);

i definicja szablonu

template <class t> ostream& operator << (ostream& out, t & obiekt) { obiekt.print(); } 

Niby różnica jest niewielka ponieważ zamiast "void funkcja()" napisałem "ostream& operator<<()" jednak program się nie skompiluje bo linker nie może znaleźć na przykład funkcji "operator << (ostream& , Test<1>& )".

Da się to jakoś obejść?

0

sorry, ze dopiero po prawie tygodniu, ale mialem malo czasu:/

z jakąś globalną funkcją szablonową, która jak wiemy może mieć jako parametr tylko typ

na jakiej podstawie tak twierdzisz?
zerknij na to, jest poprawne, msvc2008:

#include <iostream>

template<typename T, size_t S> struct arie{ T value[S]; };

template<typename T, size_t S>
arie<T, S> WTF(){ return arie<T, S>(); }

int main()
{
    std::cout << sizeof(WTF<double, 10>().value) / sizeof(double) << std::endl;
    std::cout << sizeof(WTF<double, 123456789>().value) / sizeof(double) << std::endl;
}

bo linker nie może znaleźć na przykład

to by sugerowalo, ze template sie nie 'instancjonowal' z jakiegos powodu, np. machnales sie w ktoryms fwd-decl. jezeli to friend by nie dzialal jak nalezy, to kod by sie nie skompilowal, gdyz bylby konflikt dostepu do private'ow. niestety, bedizesz musisal podac troche wiecej kodu, zebym mogl Ci dokladniej wskazac.. w kazdym badz razie, z template-function czy template-operator (to to dokaldnie samo jak juz nadmieniles) -- friendy bardzo ladnie dzialaja. problem z friendem to zwykle namespace'y, zla kolejnosc deklaracja (po, nie przed, brak fwddecl przed, etc) --- a jak juz doszlo do linkera, to na prawde, albo zaponiales napisac funkcji/dolaczyc pliku cpp, albo machnales sie w fwddecl i nie pasuje on do pozniejszej implementacji. moze masz kod lekko niedopoukladany, albo masz poukladany ale pechow i musisz RECZNIE instancjonowac template'a? jezeli wykluczyles literowki w fwddecl itp, w pliku .cpp skojarzonym z klasa Test dopisz na samym koncu:

template ostream& operator<< <> ( ostream&, Test<1>& );

patrz http://www.parashift.com/c++-faq-lite/templates.html sekcja 35.13

0

Dzięki za odpowiedź :)

sorry, ze dopiero po prawie tygodniu, ale mialem malo czasu:/

Nic się nie dzieje - ja tylko zgłębiam się w tajniki C++, nie śpieszy mi się z tym. :P

hmm... widać były jakieś zmiany bo stare g++ na moim starym PC przyjmuje tylko nazwę typu jako parametr... a teraz sprawdziłem na lapku i faktycznie przyjmuje tak jak i klasy stałe liczbowe. (Swoją drogą w jednej z książek - chyba Grębosza - jest napisane że nie może być stałych liczbowych w funkcjach - więc na pewno jakieś zmiany w tej materii musiały być).

Ok w takim razie problem/pytanie trochę się redefiniował(-o) :D

#include <iostream>

using namespace std;

template <class t>
ostream& operator<<(ostream& out, t & dupa)
{
    dupa.print();
    return out;
}

template <class t>
ostream& func(ostream& out, t & dupa)
{
    dupa.print();
    return out;
}

template <int t>
class Test
{
    public:
        void print()
        {
            cout << t;
        }
     //   friend ostream& operator<<(ostream& out, Test<t> & dupa);
     //   friend ostream& func(ostream& out, Test<t> & dupa);
};

int main()
{
    Test<1> dupa;
    func(cout, dupa);
    cout << dupa;

    return 0;
}

DLaczego nie można zadeklarować przyjaźni z takimi funkcjami szablonowymi? Przecież w szablonie klasy deklaruję sobie że ten operator/funkcja przyjmuje typ Test<t>. A wyżej szablon funkcji/operatora przyjmuje jako parametr dowolny typ drugiego argumentu a Test<t> zawiera się w zbiorze dowolnych typów (że tak się matematycznie wyrażę :P ). Dlaczego więc to nie pyra po usunięciu komentarzy? :P

0

Dlaczego więc to nie pyra po usunięciu komentarzy?

Dlatego, że t z definicji klasy to stała int, a t z definicji operatora/funkcji to jakiś typ. Daj:

template <class T1> friend ostream& operator<< (ostream& out, T1&);
template <class T1> friend ostream& func(ostream& out, T1&);

i powinno hulać.

0

btw:

template <class t>
ostream& operator<<(ostream& out, t & dupa)
{
    dupa.print();   // huh? to po co Ci OUT w parametrze skoro hardcode na COUT ?
    return out;
}

===>

template <class t>
ostream& operator<<(ostream& out, t & dupa)
{
    dupa.print(out);
    return out;
}
....
        void print(ostream & out) // inaczej, operator << jest bezsensu
        {
            out << t;
        }

jesli poszesz operaotry, to niech one spelaniaja swoje zadanie!
piszac operator << na ostream, niech operuje on na ostream.. operator taki jak podales przed chwila kompilator z radoscia przyjmie dla ofstream << dupa - ale zachowalby sie to kompletnie nieprawidlowo

0
0x666 napisał(a)

Dlaczego więc to nie pyra po usunięciu komentarzy?

Dlatego, że t z definicji klasy to stała int, a t z definicji operatora/funkcji to jakiś typ. Daj:

template <class T1> friend ostream& operator<< (ostream& out, T1&);
template <class T1> friend ostream& func(ostream& out, T1&);

i powinno hulać.

hmm... No ok ale teraz klasa zaprzyjaźni mi się ze wszystkimi operatorami << utworzonymi z szablonu funkcji... nawet z tymi z którymi nie powinna np. klasa szablonów do ciast zaprzyjaźni mi się z operatorem << dla klas szablonów stolarskich :P Bo przecież to T1, które podałeś to dowolny typ. Mam rację?

quetzalcoatl, wiem wiem... jednak ten programik pisałem byle by był jak najszybciej nie zastanawiając się zbytnio nad sensem jego działania. Chodziło tylko i wyłącznie o ukazanie sposobu działania kompilatora. Niestety kompilator to cwana bestia i nie chce być posłuszny w tym przypadku :P

0

Mam rację?

Masz, tyle że z drugiej strony Twój operator:

template <class t> ostream& operator<<(ostream&, t&)

jest równie ogólny i może dotyczyć każdej klasy (pomijam implementację). To, co podałem, było robione pod ten operator.

Wersja bardziej konkretna:

template <int T> class Test;

template <int T> ostream& operator<<(ostream& out, Test<T> &dupa)
{
	dupa.print();
	return out;
}


template <int T> class Test
{
	void print() { cout << T; }
public:
	

	friend ostream& operator<< <>(ostream& out, Test<T>&);
};

p.s. nazwy parametrów szablonu pisz dużymi literami.

0
0x666 napisał(a)

jest równie ogólny i może dotyczyć każdej klasy (pomijam implementację).

Czyli mam rozumieć że dla kompilatora jest on za ogólny ;]

0

Nie, to była odpowiedź na zarzut, że zaprzyjaźniam klasę ze wszystkimi operatorami <<.

1 użytkowników online, w tym zalogowanych: 0, gości: 1