[C++] szablony, dziedziczenie prywatne i szalone problemy

0

Problem to pewnie wina złych pomysłów... Ale w skrócie, mam klasę bazową będącą teoretycznie robolem oraz klase niby interfejsu wyższego poziomu. Obie te klasy to szablony. Trudno opisać cały ten bałagan, w każdym razie kod przykładowy wygląda tak:

template <typename T>
class BaseW
  {
    protected:
      T work(T a, T b) { /*tu żyją smoki*/};
    public:
      BaseW(){/*tu składają jaja smoki*/};
      ~BaseW(){/*tu giną smoki*/};
  }

template <typename T>
class WHorse: private BaseW<T>
  {
    protected:
      typedef T (BaseW::*wrk)(T,T);
      map<char,wrk> workers;
    public:
      WHorse()
        {
          workers['a']=BaseW<T>::work; // <-- Tu jest problem kompilacji
        }
      virtual ~WHorse(){/*nothing here, move on*/};
  }

Problem jaki się pojawia to jakoby brak funkcji w tym miejscu (np dla konkretyzacji z int), brzmi on:

no matching function for call to  WHorse<int>::work().
note: candidates are: T BaseW::work(T,T) [ with T = int ].

Może to mój błąd, ale wydaje mi się że powinno przyjąć normalnie... Co tu robie źle? może ewentualnie jakaś zmiana podejścia?

0

Mapa jako klasa nie może uzyskać dostępu do innych składowych niż publiczne, chyba że zostanie zaprzyjaźniona z daną klasą, tyczy się to także wskaźników na składowe. Do tego wypadałoby przez bieżącą, zaprzyjaźnioną klasę się do dziedziczonych metod odwoływać. To powinno zadziałać na wszystkich kompilatorach:

template <typename T>
class WHorse: private BaseW<T> {
protected:
    typedef T (BaseW<T>::*wrk)(T,T);
    map<char,wrk> workers;

    template < class, class, class, class > 
    friend class map;
public:
    WHorse() {
        workers['a'] = &BaseW<T>::work;
    }
    virtual ~WHorse() {/*nothing here, move on*/}
};

Może nie wygląda za pięknie ale z ISO jest zgodne i nawet durne kompilatory to łykają, swoją drogą G++ jest chyba najbardziej tolerancyjny.

A w ramach promocji kolejna ciekawostka z g++, która generuje uroczy komunikat o błędzie:

template<class> class bo_to_zla_struktura_byla { friend int main(); };

<font color="#7F7F7F7F" size="1">hm, (w)horse...</span>

0
Johnny_Bit napisał(a)

może ewentualnie jakaś zmiana podejścia?

Może coś takiego raczej:

template <typename T>
class Master
{
protected:
   class IWorker
   {
   public:
      virtual T work(T a, T b) = 0;
      virtual ~IWorker(){}
   };

   class BaseWorker : public IWorker
   {
   public:
      virtual T work(T a, T b) { /* ... */};
   };

   class ExtWorker : public IWorker
   {
   private:
      Master *master;
   public:
      ExtWorker(Master *master) { this->master = master; }
      virtual T work(T a, T b) { /* ... */; };
   };

   map<char,IWorker*> workers;
   typedef typename std::map<char,IWorker*>::iterator iterator;
public:
   Master()
   {
      workers['a'] = new BaseWorker();
      workers['b'] = new ExtWorker(this);
   }

   ~Master()
   {
      for(iterator it = workers.begin(); it != workers.end(); it++)
         delete it->second;
   }

   DoWork()
   {
      T a, b;
      for(iterator it = workers.begin(); it != workers.end(); it++)
         it->second->work(a, b);
   }
};
0

Masz dziedziczenie prywatne, co jest bezsensu. Popraw na dziedziczenie publiczne lub chronione i będzie po problemie.

0

Taka zmiana podejścia... No cóż, może być przydatna jak obecne podejście się nie spisze...

wracając do problemu:
Po zmianie już ma być wszystko ładnie, pięknie, a tu lipa. Kod:

main.cpp:

include <iostream>
#include "test.hpp"

using namespace std;

int main()
{
		WHorse<int> W;
    cout << "Hello world!" << endl;
    return 0;
}

test.hpp:

#ifndef TEST_HPP_INCLUDED
#define TEST_HPP_INCLUDED

#include <map>

template <typename T>
class BaseW
  {
    protected:
      T work(T a, T b) { /*tu żyją smoki*/return a;};
    public:
      BaseW(){/*tu składają jaja smoki*/};
      ~BaseW(){/*tu giną smoki*/};
  };

template <typename T>
class WHorse: protected BaseW<T>
  {
    protected:
      typedef T (BaseW<T>::*wrk)(T,T);
      std::map<char,wrk> workers;

      template <class, class, class, class>
      friend class std::map;

    public:
      WHorse()
        {
          workers['a']=&BaseW<T>::work;; // <-- Tu jest problem kompilacji
        }
      virtual ~WHorse(){/*nothing here, move on*/};
  };

#endif // TEST_HPP_INCLUDED

message z problemem:

test.hpp: In constructor ‘WHorse<T>::WHorse() [with T = int]’:
main.cpp:8:   instantiated from here
test.hpp:10: error: ‘T BaseW<T>::work(T, T) [with T = int]’ is protected
test.hpp:29: error: within this context

Mnie się wydaje to co najmniej dziwne... Przecież dziedziczy z base, więc do protected powinno mieć dostęp... Zmiana rodzaju dziedziczenia nie pomaga, zaprzyjaźnić maina z szablonami nie można... Właściwie pomaga tylko zmiana w base z protected na public, ale czy to miało by sens w takim wypadku? może potrzebne jakieś rzutowanie?

Edit: Po paru modyfikacjach i fajnych rozegraniach całość wygląda tak:

#ifndef TEST_HPP_INCLUDED
#define TEST_HPP_INCLUDED

#include <map>

template <typename T>
class BaseW
  {
    protected:
      T work(T a, T b) { /*tu żyją smoki*/return b;};
    public:
      BaseW(){/*tu składają jaja smoki*/};
      ~BaseW(){/*tu giną smoki*/};
  };

template <typename T>
class WHorse: protected BaseW<T>
  {
    protected:
      typedef T (BaseW<T>::*wrk)(T,T);
      std::map<char,wrk> workers;

      template <class, class, class, class>
      friend class std::map;

    public:
      WHorse()
        {
          workers['a']=&WHorse<T>::work;
        }
      virtual ~WHorse(){/*nothing here, move on*/};
      T DoWork(char cCmd, T CurrVal, T Param) { return (this->*workers[cCmd])( CurrVal, Param ); }
  };

#endif // TEST_HPP_INCLUDED

i działa jak złoto... tylko czemu nie chciał załapać BaseW zamiast WHorse..

p.s. WHorse od workhorse...

0

Dziedziczysz prywatnie + klasa bazowa nie jest z mapą zaprzyjaźniona, zaprzyjaźnienie nie działa wstecz.
@MarekR22, przy dziedziczeniu chronionym też będzie konieczne zaprzyjaźnianie klas...

0

@deus: że co? Wytłumacz mi o co ci chodzi bo nie rozumiem po co przyjaźń z mapą? Przyjaźń z mapą nie ma ty nic do rzeczy (można ją skasować).
Problem siedzi w zastosowaniu kwalifikatora i tym, że metoda jest zadeklarowana jako protected.

0

Że co? A o czym ja niby napisałem i w tym i w pierwszym poście? Jedynie klasy zaprzyjaźnione mogą uzyskać dostęp do chronionych składników, nawet jeżeli chodzi tylko o pointery na metody. Mówimy o przypadku użycia mapy, nie ogólnym.

Z punktu widzenia mapy nie ma różnicy jakie jest dziedziczenie i jaka jest widoczność jeżeli metoda nie będzie dostępna publicznie.

0

pierwszy post jest też bez sensu. Coś ci się pomieszało z tymi ograniczeniami dostępu i przyjaźniami.
mapa powinna być przyjacielem klasy jedynie w momencie, gdy jeden z argumentów tego szablonu (klucz, wartość) jest klasą nie posiadającą publicznego copy-constructora lub operator< dla klucza jest niepubliczny. To są jedyne metody klas, których używa mapa.
Tu masz w zasadzie typy proste: char (klucz) i wskaźnik na metodę (wartość), więc mapa ma dostęp do wszystkiego co potrzebuje.

0

Bez sensu to jest to Twoje trucie... Polecam douczyć się jak wyglądają prawa dostępu i wskaźniki ma metody. Nie, mapa nie ma dostępu, wartość jest wskaźnikiem na element niepubliczny - nic poza klasą i zaprzyjaźnionymi z nią nie może tego używać.
Nie ma znaczenia gdzie ta mapa jest... Spróbujem z najprostszym przykładem:

class zuo {
public:    void publiczna() {};
protected: void chroniona() {};
};

int main() {
  void(zuo::*metoda1)() = &zuo::publiczna; // przejdzie, metoda jest dostępna
  void(zuo::*metoda2)() = &zuo::chroniona; // nie przejdzie, nie jest dostępna publicznie
}

A napisać napisałeś prawie dobrze... Wartość musi być elementem publicznie dostępnym. Ja uważam sprawę za zakończoną, jak chcesz się dalej ośmieszać to pogadaj z Ranidesem lub quetzalcoatlem.

0

eee a gdzie tu twoja mapa i przyjaźń z mapą?
Sorry, ale nic nie mam do ciebie, ale moim zdaniem coś namieszałeś - może najpierw przeczytaj swoje posty.

deus napisał(a)

Mapa jako klasa nie może uzyskać dostępu do innych składowych niż publiczne, chyba że zostanie zaprzyjaźniona z daną klasą, ...

0

Co ma do tego mapa? To, że zachodzi identyczna relacja. Mapa nie może wykonywać operacji na pointerze do metody, która nie jest względem niej publiczna. Poprzedni przykład w wersji z mapą:

int main() {
  std::map<bool, void(zuo::*)()> dziala;
  dziala[true]  = &zuo::publiczna;
  dziala[false] = &zuo::chroniona;
}

Otrzymasz piękną informację, że metoda nie jest dostępna dla tej operacji. Nooo, trochę mocno uproszczone ale przedstawia sytucję z pierwszego postu w tym wątku.

0

no to wszystko jasne. Nie przeczytałeś własnych postów.
A tu masz uproszczenie problemu pokazujące, że sprawa nie jest do końca jasna (twoje przykłady nijak się mają do tego problemu):

#include <map>

class BaseW
    {
protected:
    int work( int a, int b )
	{
	return b;
	}
    };

class WHorse: public BaseW
    {
protected:
    typedef int (BaseW::*wrk)( int, int );
    std::map< char, wrk > workers;

    int work( int a, int b ) // ta metoda, żeby jeszcze zagmatwać i pokazać, że problem nie jest oczywisty i prosty
	{
	return a;
	}

public:
    WHorse()
	{
	int c;
	c = BaseW::work( 2 ,3 ); // to się kompiluje
	c = work( 2, c );
	wrk pointer = &BaseW::work; // to się NIE kompiluje! Ale czemu?
       // przez dodatkową metodę nie można posłużyć się &WHorse::work (też będzie błąd, ale innej natury)
	workers[ 'a' ] = pointer; // to się kompiluje
	}

    int DoWork( char cCmd, int CurrVal, int Param )
	{
	return (this ->* workers[ cCmd ])( CurrVal, Param );
	}
    };

// edit: spróbowałem inny kompilator (borlanda) i jest OK, czyli jest to po prostu błąd w MinGW.

0

oj.. widzisz.. dziedziczenie i prawa dostepu nie sa tak trywialne jak by sie zdawac moglo!

achtung: visual studio 2008, IMHO, w tym przypadku zachowanie 100% zgodne z std. ale przyznam, nie sprawdzalem.. porownuje z wlasna pamieci

btw. specjalnie okroilem kod do minimum ktore eksponuje te same problemy - np. wywalilem WHorse::work, ktore NIC nie zmienia w kontekscie proby pobrania wskaznika na basew::work

kod:

class BaseW
{
protected:  ///#
    int work( int a, int b ) { return b; }
    int x;
};

class Test : public BaseW { };

class WHorse : public BaseW
{
public:
    /// ---pastehere---
};

wynik: kompiluje sie.

a teraz 'wcinki' - metody z przykladow wklej w zaznaczone miejsce

przyklad 1)

    void test1()
    {
        int c;
        c = work( 2, c );  ///1
        c = BaseW::work( 2 ,3 ); ///2
    }

wynik kompilacji: ok

info:

  • w miejscu (1) klasa WHorse (:public BaseW => protected base members 'widoczne' jako protected, dziedziczenie public nic zmienilo) ma prawo wywolac odziedziczona metode, konkretnie - wywolac metode 'work' ktora zostanie przetlumaczona na 'basew::work', gdyz WHorse nie definiuje wlasnej metody work.
  • w miejscu (2) klasa WHorse ma prawo wywolac metode basew::work na dokladnie tych samych prawach. linie (1) i (2) niczym sie nie roznia!

przyklad 2)

    void test2()
    {
        typedef int (BaseW::*wrk)( int, int );
        wrk pointer = &BaseW::work; //3
    }

wynik kompilacji: blad

1>Compiling...
1>x.cpp
1>c:\poligon\4p\137015\x.cpp(15) : error C2248: 'BaseW::work' : cannot access protected member declared in class 'BaseW'
1>        c:\poligon\4p\137015\x.cpp(4) : see declaration of 'BaseW::work'
1>        c:\poligon\4p\137015\x.cpp(2) : see declaration of 'BaseW'

info:

  • w miejscu (3) klasa WHorse nie ma prawa dobierac sie do metody work. metoda ta nie jest widziana jako publiczna i nikt, ale to nikt poza klasa BaseW nie ma prawa operowac na niej, za wyjatkiem:
    --- jej friends, ktore maja taki sam dostep do wszystkiego jak ona sama
    --- dzieci, ktorym zezwala sie na wywolywanie jej metod w kontekscie obiektow swojej klasy [patrz przyklad 3] i odczyt/zapis jej pol [patrz przyklad 4]

przyklad 3)

    void test3()
    {
        work(1, 1); ///4
        Test ttt;
        WHorse hhh;
        hhh.work(1, 1); ///7
        ttt.work(1, 1); ///5
        BaseW* ptr = &ttt;
        ptr->work(1,1); ///6
    }

wynik kompilacji: blad

1>Compiling...
1>x.cpp
1>c:\poligon\4p\137015\x.cpp(16) : error C2248: 'BaseW::work' : cannot access protected member declared in class 'BaseW'
1>        c:\poligon\4p\137015\x.cpp(4) : see declaration of 'BaseW::work'
1>        c:\poligon\4p\137015\x.cpp(2) : see declaration of 'BaseW'
1>c:\poligon\4p\137015\x.cpp(18) : error C2248: 'BaseW::work' : cannot access protected member declared in class 'BaseW'
1>        c:\poligon\4p\137015\x.cpp(4) : see declaration of 'BaseW::work'
1>        c:\poligon\4p\137015\x.cpp(2) : see declaration of 'BaseW'

info:

  • w miejscu oznaczonym (5) okazuje sie ze .. WHorse nie ma prawa do metody work! czemu? poniewaz 'protected' dla member'a z bazy mowi, ze dzieci maja prawo ja wywolac w kontekscie swojej klasy. Klasa WHorse nie ma nic do klasy Test!
  • jak widac pare linii nizej, w miejscu (6) - nawet chamska proba przerzutowania Test na BaseW i wywolania metody work w scopie klasy BaseW sie nie udaje --- scope klasy BaseW to nie scope WHorse, wiec nie ma tutaj prawa wywolania.
  • i jak widac na miejscu (4) i (7) ktorego kompilator sie nie czepia, wolanie basew::work w scopie WHorse jest poprawne

przyklad 4)

    void test4()
    {
        x = 1; ///4'
        WHorse hhh;
        hhh.x = 1; ///7'
        Test ttt;
        ttt.x = 1; ///5'
        BaseW* ptr = &ttt;
        ptr->x = 1; ///6'
    }

wynik kompilacji: blad

1>Compiling...
1>x.cpp
1>c:\poligon\4p\137015\x.cpp(19) : error C2248: 'BaseW::x' : cannot access protected member declared in class 'BaseW'
1>        c:\poligon\4p\137015\x.cpp(5) : see declaration of 'BaseW::x'
1>        c:\poligon\4p\137015\x.cpp(2) : see declaration of 'BaseW'
1>c:\poligon\4p\137015\x.cpp(21) : error C2248: 'BaseW::x' : cannot access protected member declared in class 'BaseW'
1>        c:\poligon\4p\137015\x.cpp(5) : see declaration of 'BaseW::x'
1>        c:\poligon\4p\137015\x.cpp(2) : see declaration of 'BaseW'

info:

  • jak widac, sytuacje (4'/5'/6'/7') dla proby zapisu protected pol klasy bazowej daja dokladnie taki sam efekt jak w przykladzie 3) dla sytuacij analogicznych

przyklad 5) - juz tylko dla formalnosci

    void test5()
    {
        typedef int (BaseW::*zmienna);
        zmienna pointer = &BaseW::x; //8
    }

wynik kompilacji: blad

1>Compiling...
1>x.cpp
1>c:\poligon\4p\137015\x.cpp(16) : error C2248: 'BaseW::x' : cannot access protected member declared in class 'BaseW'
1>        c:\poligon\4p\137015\x.cpp(5) : see declaration of 'BaseW::x'
1>        c:\poligon\4p\137015\x.cpp(2) : see declaration of 'BaseW'

info:

  • jak sie mozna bylo spodziewac, WHorse nie ma dostepu do x w scopie BaseW w miejscu (8)

ale ale ale.. troche pomyslmy: przeciez dziedziczenie (ktore jest wrecz :public) mowi, ze WHorse jest BaseW i ze posiada metode work (ktora bedzie dla 'klientow klasy' widoczna jako protected, tak jak w bazie)
...wiec... uwaga.. to bedzie proste, ale i zawile:
skoro WHorse odziedziczylo work to .. WHorse ma metode work.

przyklad 6)

    void test6()
    {
        typedef int (WHorse::*zmienna);
        zmienna pointer = &WHorse::x; //9
    }
    void test7()
    {
        typedef int (WHorse::*wrk)( int, int );
        wrk pointer = &WHorse::work; //10
    }

wynik kompilacji: ok

info:

  • czego to dowodzi? ano.. ze WHorse ma prawo operowac w 100% na swojej metodzie work, ktora, badz co badz dostalo z klasy bazowej.. ugh.
  • to co naprawde ten przyklad pokazuje, to to, ze odwolywanie sie doczegos poprzez klase WHorse nie jest tym samym co przez BaseW, mimo ze WHorse jest obiektem BaseW. oznacza to wprost, ze mimo iz WHorse "to tez BaseW", to jednak scope klasy WHorse jest innym scopem niz BaseW, ma inne prawa niz on, i vice versa i.. reszte implikacji to juz moze zostawie do domyslu, bo moznaby ksiazke napisac pewnie

edit:

// edit: spróbowałem inny kompilator (borlanda) i jest OK, czyli jest to po prostu błąd w MinGW

z moich doswiadczen wynika, ze kompilatory Borlanda ..hm.. sa kiepskimi odwzorowaniami standardu. kompilatory z pod znaku gnu oraz, ptfu, microsoftu, trzymaja sie ich o wiele lepiej.
no, przynajmniej w tych podstawowych rzeczach nie tykajacych sie rozwiazywania zagmatwanych templateow.. tutaj podejrzewam ze i msoft i borland rowno odstaja i jedynie moze comeau jest ok:) niestety, jeszcze nie nabylem..

0

quetzalcoatl, Twój post wyjaśnił mi zasady dziedziczenia i co się dzieje w środku lepiej niż jakakolwiek książka, jakikolwiek prof., dr hab., dr, mgr inż., mgr itd [browar] . Teraz rozumiem (albo przynajmniej myślę że rozumiem) to wszystko znacznie lepiej. Powinieneś chyba napisać jakąś książkę, bo taki sposób przedstawienia wiedzy cholernie mi się podoba i na pewno by znacznie wspomógł uczenie się nie tylko mnie [browar] . Teraz w końcu nie będą mnie dziwiły głupie błędy przy dziedziczeniach wynikające z mojej głupoty [wstyd] . Dzięki, obym więcej używał tak dobrej wiedzy i inni również, a Tobie należy się więcej niż browar [browar] .

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