[C++] przyjaźń zwykłej klasy z szablonową

0

Napotkałem na problem składniowy, którego nie umiem rozwiązać. Konstrukcja, którą przedstawiam jest okrojona w porównaniu do projektu, który próbuję stworzyć - więc proszę, nie piszcie: zamiast tego stwórz klasy w takiej i takiej hierarchii. Hierarchia jest bardziej skomplikowana, i trochę musiałem pomyśleć, żeby ująć całość tak prosto, jak poniżej (mam nadzieję, że prosto).

Mam szablon klasy, który używa klasy Pojemnik. Klasa pojemnik jest zdeklarowana gdzieś niżej. Funkcje składowe Pokazywacza są zdefiniowane jeszcze niżej.

class Pojemnik;
    
template<typename Typ>
class Pokazywacz {
    public:
        void pokaz(Pojemnik& arg);
    };

Klasa Pojemnik jest zwykłą klasą. Pokazywacz może do woli korzystać w swoich funkcjach z publicznych właściwości Pojemnika - miło. Problem w tym, że chcę grzebać również z właściwości prywatnych. Muszę zadeklarować przyjaźń, i głupia sprawa - nie wiem jak:

class Pojemnik {
    private:
        int value;
    public:
        // taka deklaracja działa (przyjaźń z int'owym Pokazywaczem)  ale to za mało
        friend class Pokazywacz<int>;

        // błąd: wymagany argument dla szablonu
        friend class Pokazywacz;
    
        // błąd: częściowa specjalizacja szablonu nie specjalizuje żadnych argumentów
        template<class typ> friend class Pokazywacz<typ>;
    
       // błąd: oczekiwane [unqualified-id] przed słowem template (co oznacza unqualified-id?)
        friend template<class typ> class Pokazywacz<typ>;
    };

dla ścisłości podam jeszcze, jak zdefiniowana jest funkcja:

template<typename Typ>
void Pokazywacz<Typ>::pokaz(Pojemnik& arg) {
    // no i tu oczywiście jest błąd: int Pojemnik::value jest w tym kontekście prywatny
    cout << arg.value << endl;
    }

Przepraszam, za przyklejenie tego, ale zależy mi na rozwiązaniu w dowolnym czasie - jak ktoś napisze za x tygodni, jak zadeklarować tę nieszczęsną przyjaźń to też będzie super, a po takim czasie to pytanie już dawno spadłoby gdzieś na n-tą stronę. (nie śpieszy mi się, bo problem można rozwiązać siłowo: upubliczniając problematyczne składniki, ale jeśli da się tego uniknąć, to wolałbym z tego skorzystać).

0

wydaje mi sie ze po prostu:

template<class typ="typ"> friend class Pokazywacz;

ale pamietam jak z jakies pol roku temu trafilem przy okazji przenoszenia kodu z g++ na msvc ze z friend i template byla jakas specyficzna skladnia w jakims specyficznym przypadku (ano.. pamietam az TAK dokladnie..) i poszukam jeszcze

edit:
tak, dokladnie tak.. ten kod wlasnie puscilem przez G++ i byl ok, wynik: 5 :)

#include <iostream>

template<class T> class Kumpel;
template<class T> class Ja;

template<class T>
class Ja
{
        T const wartosc;
public:
        Ja(T const & arg):wartosc(arg){}

        template<class S> friend class Kumpel;
};

template<class T>
class Kumpel
{
public:
        T const & getmoja(Ja<T> const & ja){return ja.wartosc;}
};

int main()
{
        int x = 5;
        Ja<int> ja(x);
        Kumpel<int> kumpel;
        std::cout << kumpel.getmoja(ja) << std::endl;
}
0

Dzięki wielkie.

Mogłem od razu wpaść, że to będzie proste, tylko ja jestem zamot [wstyd] Licho... przecież to prawie tak, jak w trzecim podejściu miałem, mogłem jeszcze chwilę pokombinować...

ale pamietam jak z jakies pol roku temu trafilem przy okazji przenoszenia kodu z g++ na msvc ze z friend i template byla jakas specyficzna skladnia w jakims specyficznym przypadku

A bardzo jestem ciekaw, jak ci się chce, to poszukaj. Biorąc pod uwagę akrobatykę, jaką teraz robię, to jest duża szansa, że się na to natknę.

0

przekopalem stary kod i znalazlem -- ale to nie chodzilo friend, tylko o typedef i drobna roznice w 'inteligencji' g++ i msvc :)

a tak wlasciwie nie tyle ineligencji co o sposob parsowania definicji klasy..

g++ zaczyna od zauwazenia nazwy klasy, zapisania ze taka istnieje, zauwazenia definiowanych przez nia nazw roznych jej skladnikow (pol, metod, typow), zapamietania ze takie nazwy sa w klasie, a dopiero potem zaczyna badac typy poszczegolnych skladnikow.

msvc2005 zaczyna od zauwazenia nazwy klasy, zapisania ze taka istnieje, zauwazenia definiowanych przez nia nazw typow, dokladnego przeanalizowania ich, a potem przejscia do innych skladnikow jak pola i metody..

tak wiec, wezmy na przykald ksiazke:
"C++ Templates: The Complete Guide"
(ksiazka dostepna jako ebook: http://it-reading.org/ebook/c++%20templates/)

i spojrzmy na sam koniec rozdzialu 20.2.6 "Simple Invasive Counter template" (http://it-reading.org/ebook/c++%20templates/content/0201734842_ch20lev1sec2.html#ch20lev2sec14)

ten kod w wielkim skrocie:

template<typename objT,     //typ obiekt ktory bedzie zarzadzany
         typename counterT,    //typ pola bedacego licznikiem referencji
         counterT objT::*counterpointer>   //wskaznik na membera bedacego lciznikiem referencji
class MemberReferenceCount
{ .... }

template< .. >
class CountingPtr  //klasa bedaca wskaznikiem z licznikiem referencji, korzysta z MemberReferenceCount
{ ... }                  

class ManagedType   //klasa obiektu ktorym chcemy zarzadzac poprzez ow countingptr
{
     size_t counter;
public:
     typedef CountingPtr<
        ManagedType, MemberReferenceCount<
               ManagedType, size_t, &ManagedType::counter>> Ptr;
}

idea invasive counter brzmi: chcemy zliczyc referencje korzystajac z pola krote juz istnieje w klasie. nie chcemy dokladac pokreconego dziedzicenia, wspolnej zliczalnej bazy itp. poprsotu - mamy pole i chcemy w nim zliczac. Dodatkowo: to ma byc pole ktore my wskazemy, a nie jakas narzucona nazwa czy typ..

klasa TheObject chce zdefiniowac typedef'a ktory uprosci bardzo korzystanie z typu CountingPtr specjalizowanego na jej uzytek. z razji tego drobnego faktu ze pole counter jest private (no bo jak, publicznie? wg. idei reference countingu obiekt sam ma sie kontrolowac i zniszczyc a nie pozwalac innym swobodnie swoj counter modyfikowac!) --- typedef ten musi byc w tej klasie. inaczej przeciez nie bedzie w ogole dostepu do &Ble::counter! Ptr dzieki podaniu w specjalizacji tego wskaznika na zmienna, jak juz bedzie mial konkretny obiekt - bedzie mogl sie poprzez ten wskaznik odwolac do tej wlasnie zmiennej...

wszystko ladnie logicznie i pieknie.. ale bierzemy MSVC2005 (o wczesniejszych nie wspominajac..) i mamy kongo:

Pamietacie analize definicji klasy wg msvc co opisywalem na poczatku? msvc2005 zauwaza class ManagedType, potem zauwaza typedef ManagedType::Ptr, chce rozwinac jego typ, zauwaza ze to template i ze jeden z parametrow jest konkretna wartoscia - &ManagedType::counter, wiec probuje wyliczyc te wartosc.. i pupa. Bo jeszcze nie zdazyl przeanalizowac jakie nazwy metod/pol sa w ManagedType i symbolu ManagedType::counter dla neigo nie istnieje!

a g++ (ktore najpierw skanuje definicje w poszukiwaniu nazw definiowanych przez kalse a potem dopiero konkretyzuje typy) sobie z tym doskonale radzi..

czesciowe rozwiazanie problemu dla msvc wyglada tak:

class ManagedType
{
public:
     size_t counter;
}

typedef CountingPtr<
     ManagedType, MemberReferenceCount<
                ManagedType, size_t, &ManagedType::counter>> ManagedType_Ptr;

i potem wszedzie zamiast Ble::Ptr stosowac Ble_Ptr. chyba nie trzeba komentowac zarowno faktu upublicznienia zmiennej coutera oraz "rozwiazania nazwo-manglingowego" (bo nie mozna samego Ptr zostawic, bo przeciez co trzecia klasa moze chciec taki typ zdefiniowac)

0

woow, warto było chwilę poczekać na coś takiego. Myślę, że temat został wyczerpany, zwłaszcza, że dałeś linka do strony, której nie znałem, a na której można sporo ciekawego znaleźć czytadła.

Jeszcze raz dzięki, wątek za zamknięty uważam.

// to go odklec trzeba bylo - Ł

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