Autor Wątek: No matching function for call to FuncB  (Przeczytany 3491 razy)

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Maj 28, 2016, 10:24:59
Trafiłem na problem w C++, który mnie trochę przyblokował. Zgłupiałem i nie mogę znaleźć rozwiązania... W maksymalnym uproszczeniu sytuacja wygląda następująco...

Są dwie klasy: bazowa ClassA i dziedzicząca po niej ClassB.
class ClassA
{
}

class ClassA : public ClassB
{
}

Mam dwie podobne funkcje różniące się tylko parametrem.
void FuncA(ClassA* a)
{
}

void FuncB(ClassA** a)
{
}

Teraz gdy odpalam te funkcje z obiektem ClassB jako parametr:
ClassB* objB = new ClassB();

FuncA(objB);
FuncB(&objB);
przy wywołaniu FuncB(&objB) dostaję błąd kompilacji z komunikatem:
"No matching function for call to FuncB."

Dodam jeszcze, że gdy jako parametru użyję obiektu ClassA to nie ma problemów.
ClassA* objA = new ClassA();

FuncA(objA);
FuncB(&objA);


Naprawdę klasa, która dziedziczy po bazowej w tym przypadku nie jest także ta bazową?
Ma ktoś jakiś pomysł czy mogę coś z tym zrobić? Ja już całkiem zgłupiałem...

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline .c41x

  • Użytkownik
    • homepage

  • +2
# Maj 28, 2016, 11:33:22
Naprawdę klasa, która dziedziczy po bazowej w tym przypadku nie jest także ta bazową?
objB nie może być automatycznie przekonwertowany na ClassA**. Sam wskaźnik do klasy pochodnej jest automatycznie konwertowany do klasy bazowej, ale wskaźnik do wskaźnika to już co innego. Teoretycznie możesz zkonwertować to tak: FuncB((ClassA**)&objB), ale takie haki wyglądają bardzo podejrzanie :)
Jeśli interesuje cię dlaczego taka konwersja jest nielogiczna/potencjalnie niebezpieczna to zastanów się nad takim kawałkiem kodu:
ClassB* objB = new ClassB();
ClassA** p = &objB; // zakładam że to zadziała automatycznie
*p = new ClassA();
objB->foo(); // ups

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Maj 28, 2016, 23:02:51
Dzięki za odpowiedź.
Faktycznie FuncB((ClassA**)&objB) działa poprawnie. Ale zgodzę się, że wygląda naprawdę paskudnie. :( Trochę mnie ten problem przyblokował, bo w oparciu o moje błędne założenie zaprojektowałem sobie dosyć istotny element mojego pseudo-mini-silnika. Muszę teraz wykombinować coś innego, bo po zastosowaniu takiego zapisu ciężko byłoby mi zasnąć. ;)

Offline Xion

  • Moderator
    • xion.log

  • +1
# Maj 29, 2016, 02:23:56
Cytuj
Naprawdę klasa, która dziedziczy po bazowej w tym przypadku nie jest także ta bazową?
Bo "w tym przypadku" mówisz o wskaźnikach, nie klasach. To trochę tak jakby spodziewać się, że int* powinien być niejawnie konwertowany do float*, bo przecież int do float się konwertuje.

Cytuj
Teoretycznie możesz zkonwertować to tak: FuncB((ClassA**)&objB), ale takie haki wyglądają bardzo podejrzanie :)
Nie trzeba żadnych hacków. Skorzystaj albo z sensownego operatora castowania zamiast wolnej amerykanki rodem z C, albo wprowadź dodatkową zmienną:
ClassB* objB = new ClassB();
ClassA* objA = objB;
FuncB(&objA);

Cytuj
Jeśli interesuje cię dlaczego taka konwersja jest nielogiczna/potencjalnie niebezpieczna to zastanów się nad takim kawałkiem kodu:
Ten kod to żadne uzasadnienie. Jedyne co on demonstruje to aliasing, który w C++ możliwy przy dowolnego "poziomu" wskaźnikach:
Foo* foo = new Foo();
Foo* foo2 = foo;
delete foo2;
foo->bar(); // boom
Jest to oczywiście problem (i to spory), ale niezbyt związany (w C++) z systemem typów i konwersjami.

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Maj 29, 2016, 17:30:01
To trochę tak jakby spodziewać się, że int* powinien być niejawnie konwertowany do float*, bo przecież int do float się konwertuje.
Ten bardzo obrazowy przykład ładnie pokazuje absurd mojego błędnego założenia. :)

Nie trzeba żadnych hacków. Skorzystaj albo z sensownego operatora castowania zamiast wolnej amerykanki rodem z C, albo wprowadź dodatkową zmienną:
ClassB* objB = new ClassB();
ClassA* objA = objB;
FuncB(&objA);
Taka zmienna tymczasowa nie wygląda już źle. Gdybym to jeszcze ubrał w jakiś zgrabny "define" to byłoby nawet wygodne w użyciu. Chyba tak zrobię.
Dzięki za radę! :)

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Maj 29, 2016, 22:24:01
Ech... Niestety obiekt tymczasowy nie rozwiązuje mi problemu, bo w innej funkcji potrzebuję wskaźnika na "oryginalną" zmienną. :(

Upraszczając wszystko do granic możliwości problem wygląda tak:
class ClassA
{
}

class ClassA : public ClassB
{
}

ClassB* objB = ClassB();
vector<ClassA**> refs;

refs.push_back(&objB);
Błąd wyskakuje przy próbie wrzucenia wskaźnika na objB (ClassB) do wektora wskaźników na obiekty typu ClassA.

Muszę niestety mieć listę wskaźników na obiekty żeby je potem móc modyfikować ("nullować") w razie potrzeby.

Chyba coś takiego się nie uda, nie?
« Ostatnia zmiana: Maj 29, 2016, 22:36:44 wysłana przez MDW »

Offline Dab

  • Redaktor
    • blog

# Maj 29, 2016, 23:07:04
Cytuj
vector<ClassA**>

Czemu? :)

Cytuj
Muszę niestety mieć listę wskaźników na obiekty żeby je potem móc modyfikować ("nullować") w razie potrzeby.

Czy to czego potrzebujesz to nie czasem std::shared_ptr + std::weak_ptr?

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Maj 29, 2016, 23:12:03
Czy to czego potrzebujesz to nie czasem std::shared_ptr + std::weak_ptr?
Bardzo pięknie mnie wybadałeś i trafnie wydedukowałeś. Próbuję zrobić własne słabe referencje. :) Niestety na platformie na której mi zależy nie mam tak nowego C++.

Offline Xion

  • Moderator
    • xion.log

# Maj 30, 2016, 02:44:45
shared_ptr nie używa żadnej nowej językowej magii, to po prostu zwykła struktura z operatorami. Możesz zwyczajnie skopiować sobie jej kod, albo boost::shared_ptr na której się zresztą opiera.

Względnie możesz użyć lepszego języka który takie błahostki ma wbudowane.

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Maj 30, 2016, 14:23:52
shared_ptr nie używa żadnej nowej językowej magii, to po prostu zwykła struktura z operatorami. Możesz zwyczajnie skopiować sobie jej kod, albo boost::shared_ptr na której się zresztą opiera.

Względnie możesz użyć lepszego języka który takie błahostki ma wbudowane.

W czasie szukania rozwiązania gdzieś trafiłem na implementację shared_ptr/weak_ptr ale jakoś nie widziałem tam żadnej listy wskaźników na słabe referencje, które trzeba “wyNULLować” w przypadku gdy zostanie skasowana ostatnia silna referencja. Dlatego bardziej się tym nie zainteresowałem.

Offline Xion

  • Moderator
    • xion.log

# Maj 31, 2016, 22:10:00
Taka lista nie jest potrzebna. Do implementacji refcountujących wskaźników wystarczą tylko dwa liczniki (strong refcount i weak refcount) do których dostęp mają zarówno słabe jak i silne referencje.

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Czerwiec 09, 2016, 00:10:30
Taka lista nie jest potrzebna. Do implementacji refcountujących wskaźników wystarczą tylko dwa liczniki (strong refcount i weak refcount) do których dostęp mają zarówno słabe jak i silne referencje.
Tak byłoby fajnie. Ale nie wyobrażam sobie jakim cudem kasowana ostatnia silna referencja miałaby "zNULLować" wszystkie zmienne trzymające słabe referencje. No musi mieć na nie wskaźniki. :)

Offline Kos

  • Użytkownik
    • kos.gd

# Czerwiec 09, 2016, 00:14:32
Tak byłoby fajnie. Ale nie wyobrażam sobie jakim cudem kasowana ostatnia silna referencja miałaby "zNULLować" wszystkie zmienne trzymające słabe referencje. No musi mieć na nie wskaźniki. :)
No nie, ostatnia silna referencja sprząta obiekt i zeruje wskaźnik w strukturze pośredniej na którą pokazują wszystkie słabe referencje. Ostatnia słaba referencja sprzata strukturę pośrednią.

Offline MDW

  • Użytkownik
    • www.encore-games.com

# Czerwiec 09, 2016, 01:17:18
No nie, ostatnia silna referencja sprząta obiekt i zeruje wskaźnik w strukturze pośredniej na którą pokazują wszystkie słabe referencje. Ostatnia słaba referencja sprzata strukturę pośrednią.
Aaa, czyli trzeba mieć pośrednią strukturę. No wtedy to ma sens. Zastanawiam się tylko gdzie powinien być trzymany ten obiekt pośredniczący. To powinien być jakiś dodatkowy twor trzymający wszystkie obiekty pośredniczące?

Offline Xion

  • Moderator
    • xion.log

# Czerwiec 09, 2016, 10:00:09
Nie, to dodatkowy kawałek pamięci dla każdego obiektu. Może być nawet zaalokowany tuż przed/za samym obiektem.