C++ templates med arv. Deklarera copy constructor.

Permalänk

C++ templates med arv. Deklarera copy constructor.

Hej.

Jag sitter och bråkar med templates och försöker komma fram till hur man deklarerar en konstruktor som tar en derived typ som argument.
Typ såhär:

int main() { const Container<DerivedA> * aContainer = new Container<DerivedA>(); Container<DerivedB> * bContainer = new Container<DerivedB>(*aContainer); }

Följande är som jag trode att man skulle göra men hur jag än rotar runt så får jag det inte att kompilera.

class BaseClass {}; class DerivedA : BaseClass { public: DerivedA(const BaseClass &); }; DerivedA::DerivedA(const BaseClass &){} class DerivedB : BaseClass { public: DerivedB(const BaseClass &); }; DerivedB::DerivedB(const BaseClass &){} template<class T> class Container { public: Container(); Container(const Container &); ~Container(); T * obj; }; template<class T> Container<T>::Container() { obj = new T(); } template<class T> Container<T>::Container(const Container<T> & in) { obj = new T(in.obj); } int main() { const Container<DerivedA> * aContainer = new Container<DerivedA>(); Container<DerivedB> * bContainer = new Container<DerivedB>(*aContainer); }

Som det är nu så får jag följande felmedelande:

../src/test.cpp:57:72: error: no matching function for call to ‘Container<DerivedB>::Container(const Container<DerivedA>&)’ ../src/test.cpp:57:72: note: candidates are: ../src/test.cpp:52:19: note: Container<T>::Container(const T&) [with T="DerivedB"] ../src/test.cpp:52:19: note: no known conversion for argument 1 from ‘const Container<DerivedA>’ to ‘const DerivedB&’ ../src/test.cpp:41:19: note: Container<T>::Container(const Container<T>&) [with T="DerivedB"] ../src/test.cpp:41:19: note: no known conversion for argument 1 from ‘const Container<DerivedA>’ to ‘const Container<DerivedB>&’ ../src/test.cpp:36:19: note: Container<T>::Container() [with T="DerivedB"] ../src/test.cpp:36:19: note: candidate expects 0 arguments, 1 provided

Koden i main kan jag inte ändra på utan det är i Container klassen jag måste på något sätt ändra konstruktorn (main-delen hör till en "driver" som ett testprogram kör för att testa så att jag kodat rätt så den får jag inte röra).

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

Problemet (som du kanske har märkt) kommer från raden:

Container<DerivedB> * bContainer = new Container<DerivedB>(*aContainer);

Konstruktorn förväntar sig typen DerivedB, det är ju den klassen du instantierar, men du ger den typen DerivedA.
Hur man ska lösa det är inte helt uppenbart för mig.

Permalänk
Medlem

trots gemensam basklass är a och b olika klasser och inte utbytbara rakt av. gör om bcontainer till en container<baseclass> så kan du skapa den med både en container<a> och container<b>.

cheers

Permalänk
Skrivet av Mikael07:

Problemet (som du kanske har märkt) kommer från raden:

Container<DerivedB> * bContainer = new Container<DerivedB>(*aContainer);

Konstruktorn förväntar sig typen DerivedB, det är ju den klassen du instantierar, men du ger den typen DerivedA.
Hur man ska lösa det är inte helt uppenbart för mig.

Nej, inte mig heller. Skickade precis ett mail till min lärare så får vi se hur han hade tänkt att jag skulle kunna göra det.

Skrivet av helmet:

trots gemensam basklass är a och b olika klasser och inte utbytbara rakt av. gör om bcontainer till en container<baseclass> så kan du skapa den med både en container<a> och container<b>.

cheers

Problemet är ju att jag inte får göra om i main. Jag ska bygga en Container klass som ska kunna vara instanserad med en typ men kunna ta en annan typ i copy constructorn som är av samma basklass.

Egentligen är min uppgift att bygga en kalenderklass som ska kunna kompileras och köras tillsammans med en testfil som testar att min klass men jag har fastnat på precis detta problem. Så det är därför jag byggde detta enkla testfall och det är därför min main (dvs hur containern används) inte kan ändras.

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

det skulle vara intressant att se vad din lärare svarar. med lite trixande kan man få det att kompilera om du gör så att container ärver av en icke-template basklass och om obj är en medlem i bascontainern av typen baseclass. men grundpremissen (din main) känns fortfarande ganska skum.

cheers

Permalänk
Medlem
Skrivet av helmet:

det skulle vara intressant att se vad din lärare svarar. med lite trixande kan man få det att kompilera om du gör så att container ärver av en icke-template basklass och om obj är en medlem i bascontainern av typen baseclass. men grundpremissen (din main) känns fortfarande ganska skum.

cheers

Håller med. main() känns lite krystad.

Permalänk
Medlem

Kan det här verkligen stämma, du deklarerar ju en copy kontruktor, alltså kommer det inte skapas en default kontruktor vilket gör att

obj = new T();

självfallet inte kommer fungera...

Permalänk
Medlem
Skrivet av hawy:

Kan det här verkligen stämma, du deklarerar ju en copy kontruktor, alltså kommer det inte skapas en default kontruktor vilket gör att

obj = new T();

självfallet inte kommer fungera...

Precis, jag var tvungen att lägga in en default constructor explicit för att kunna kompilera (g++). Men vissa andra kompilatorer kanske lägger dit en automagiskt?

Permalänk
Skrivet av hawy:

Kan det här verkligen stämma, du deklarerar ju en copy kontruktor, alltså kommer det inte skapas en default kontruktor vilket gör att

obj = new T();

självfallet inte kommer fungera...

Skrivet av Mikael07:

Precis, jag var tvungen att lägga in en default constructor explicit för att kunna kompilera (g++). Men vissa andra kompilatorer kanske lägger dit en automagiskt?

Sorry 'bout that! Jag rensade bort en massa olika försök innan jag lade upp koden här och då råkade jag ta bort för mycket! Ni har helt rätt!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Datavetare

Hmm, vad ska du ha detta till???

Vad du måste göra är att ge din Container klass en copy-ctor som tar den andra typen som template argument. Detta kompilerar, men reinterpret_cast<> borde skrika WARNING!!!

class BaseClass {}; class DerivedA : BaseClass { public: DerivedA() {}; DerivedA(const BaseClass &) {}; }; class DerivedB : BaseClass { public: DerivedB() {}; DerivedB(const BaseClass &) {}; }; template<typename T> class Container { public: Container() : obj(new T()) {}; Container(const Container &in) : obj (new T(*in.obj)) {}; ~Container() { delete obj; }; T * obj; template<typename S> Container(const Container<S> &in) : obj(new T(*reinterpret_cast<T*>(in.obj))) {}; }; int main() { const Container<DerivedA> * aContainer = new Container<DerivedA>(); Container<DerivedB> * bContainer = new Container<DerivedB>(*aContainer); }

Edit: det är möjligt att gör lite modifieringar, du har t.ex. ärvt basklassen privat om man ändrar det till ett publikt arv

class BaseClass {}; class DerivedA : public BaseClass { public: DerivedA() {}; DerivedA(const BaseClass &) {}; }; class DerivedB : public BaseClass { public: DerivedB() {}; DerivedB(const BaseClass &) {}; }; template<typename T> class Container { public: Container() : obj(new T()) {}; Container(const Container &in) : obj (new T(*in.obj)) {}; ~Container() { delete obj; }; T * obj; template<typename S> Container(const Container<S> &in) : obj(new T(*static_cast<BaseClass*>(in.obj))) {}; }; int main() { const Container<DerivedA> * aContainer = new Container<DerivedA>(); Container<DerivedB> * bContainer = new Container<DerivedB>(*aContainer); }

Men det är egentligen inte bättre, men folk skriker mindre om det inte finns reinterpret_cast:s

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Men det är egentligen inte bättre, men folk skriker mindre om det inte finns reinterpret_cast:s

Klart det är bättre, static_cast bör ju skrika rätt ut om typerna inte matchar. Däremot undrar jag hur nödvändigt det är öht då man castar mot bas-klassen. obj(new T(*in.obj)) bör väl fungera lika bra?

..sen håller jag såklart med om att TS nog gör något galet här och bör tänka om i vilket fall.

Visa signatur

"Some poor, phoneless fool is probably sitting next to a waterfall somewhere, totally unaware of how angry and scared he's supposed to be." - Duncan Trussell

Permalänk
Datavetare
Skrivet av gibbon_:

Klart det är bättre, static_cast bör ju skrika rätt ut om typerna inte matchar. Däremot undrar jag hur nödvändigt det är öht då man castar mot bas-klassen. obj(new T(*in.obj)) bör väl fungera lika bra?

..sen håller jag såklart med om att TS nog gör något galet här och bör tänka om i vilket fall.

Naturligtvis går det lika bra utan static_cast, var jag som hade lite bråttom när jag skulle fixa ett kompileringsfel i originalet där det stod obj(new T(in.obj)) och jag insåg inte direkt att det var pekare vs referens missmatch då kompileringensfelet svamlade om unacccessable base-type som kom från privat arv som originalversionen hade och gjorde att man var tvungen att köra reinterpret_cast...

Är rätt positivt inställd till C++, men är det något som är totalt trasigt i detta språk så är det de felmeddelanden man får vid kompileringsfel när templates är inblandade

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

Tror det går att göra med "Template specialization" see http://www.cplusplus.com/doc/tutorial/templates/
Är inte säker dock.

Visa signatur

orka

Permalänk

Tack för alla svar! Då det verkar som att flera tycker jag borde tänka om så passar jag på att ge mera info innan jag sätter mig in i feedbacken. Jag förenklade mitt testfall så mycket jag kunde så att jag skulle förstå felet och jag kanske förenklade lite för mycket. :/

Egentligen är det hela en kalender så Container klassen motsvarar Calendar och BaseClass motsvarar Date, DerivedA samt DerivedB motsvarar Gregorian samt Julian som är två olika datumtyper.

koden jag har i mitt exempel i main byggde jag ut ifrån hur jag tolkade att dessa rader funkade:

... } else if(command == "newcopyother") { const size_t idx = read_idx_or_die(true); cals.push_back(new Calendar<T>(*const_cast<const Calendar<S> *>((*others)[idx]))); ...

Hela testkoden finns här:

/* * Driver for cprog06lab22. Written 2006-09-27 by Gunnar Kreitz <gkreitz> * * If you need to modify this file to solve the assignment, you're most likely * solving it the wrong way. */ #include "calendar.h" #include "julian.h" #include "gregorian.h" #include "kattistime.h" #include <vector> #include <string> #include <cstdio> #include <iostream> #include <cassert> #include <stdexcept> #include "gregorian.h" #include "julian.h" #include "calendar.h" using std::size_t; using std::cout; using std::endl; using lab2::Julian; using lab2::Gregorian; using lab2::Calendar; static int linenr = 0; // Anonymous namespace for local variables and functions namespace { /* * Class to do the calendar tests. We need to use templates since we want to * be able to use both Calendar<Gregorian> and Calendar<Julian>. The reason * for the second template argument is that we want to test assignment/copy * construction between Calendars specialized on different date types. * * The idea is to make two instances of this class, each pointing to the other * instance's cals-vector (set up by using set_companion). */ template<class T, class S> class CalendarTester { public: /* * Warning: modifies argument! */ void set_companion(CalendarTester<S,T> &companion) { others = &companion.cals; companion.others = &cals; } void handle() { char buf[101]; if(scanf("%100s", buf) != 1) assert(!"Failed reading command"); std::string command(buf); if(command == "new") { cals.push_back(new Calendar<T>()); } else if(command == "delete") { const size_t idx = read_idx_or_die(); delete cals[idx]; cals[idx] = 0; } else if(command == "newcopy") { const size_t idx = read_idx_or_die(); cals.push_back(new Calendar<T>(*const_cast<const Calendar<T> *>(cals[idx]))); } else if(command == "newcopyother") { const size_t idx = read_idx_or_die(true); cals.push_back(new Calendar<T>(*const_cast<const Calendar<S> *>((*others)[idx]))); } else if(command == "copy") { const size_t from = read_idx_or_die(); const size_t to = read_idx_or_die(); (*cals[to]) = *const_cast<const Calendar<T> *>(cals[from]); } else if(command == "copyother") { const size_t from = read_idx_or_die(true); const size_t to = read_idx_or_die(); (*cals[to]) = *const_cast<const Calendar<S> *>((*others)[from]); } else if(command == "print") { const size_t idx = read_idx_or_die(); cout << "L" << linenr << ":C" << idx << ";print" << endl <<*const_cast<const Calendar<T> *>(cals[idx]); cout.flush(); } else if(command == "set_date") { const size_t idx = read_idx_or_die(); int y, m, d; if(scanf("%d%d%d", &y, &m, &d) != 3) assert(!"Failed to read date for set_date"); cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->set_date(y,m,d) << endl; } else if(command == "add_event") { const size_t idx = read_idx_or_die(); int y, m, d; if(scanf("%100s", buf) != 1) assert(!"Failed to read eventstring for add_event"); const std::string event(buf); switch(scanf("%d%d%d", &d, &m, &y)) { case 0: case EOF: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->add_event(event) << endl; break; case 1: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->add_event(event, d) << endl; break; case 2: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->add_event(event, d, m) << endl; break; case 3: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->add_event(event, d, m, y) << endl; break; default: assert(!"Unreachable code (add_event switch)"); } } else if(command == "remove_event") { const size_t idx = read_idx_or_die(); int y, m, d; if(scanf("%100s", buf) != 1) assert(!"Failed to read eventstring for remove_event"); const std::string event(buf); switch(scanf("%d%d%d", &d, &m, &y)) { case 0: case EOF: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->remove_event(event) << endl; break; case 1: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->remove_event(event, d) << endl; break; case 2: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->remove_event(event, d, m) << endl; break; case 3: cout << "L" << linenr << ":C" << idx << ";" <<cals[idx]->remove_event(event, d, m, y) << endl; break; default: assert(!"Unreachable code (remove_event switch)"); } } else if(command == "set_k_time") { time_t t = 0; /* No standardized way to read time_t with sca nf */ if(!(std::cin >> t)) assert(!"Can't read time"); set_k_time(t); } else { fprintf(stderr, "Got bad command: %s\n", command.c_str()); assert(!"Unknown command"); } } /* * * There is no way in standard C++ to read a size_t. The way we solve it here is to * * read it as an unsigned long. This program will not be used with too large inputs. * * Another way would be to use size_t ret; scanf("%zu", &ret); which is legal in C99 * * and thus most likely will be OK in C++0x. It's already supported by most compilers. * * If it was changed to using scanf instead, the sync_with_stdio(false) * * would need to be removed. * */ size_t read_idx_or_die(bool other = false) { unsigned long ret; std::cin >> ret; assert(std::cin); if(other) { assert(ret < others->size()); assert((*others)[ret] != 0); } else { assert(ret < cals.size()); assert(cals[ret] != 0); } return ret; } friend class CalendarTester<S,T>; private: std::vector<Calendar<T> *> cals; std::vector<Calendar<S> *> *others; }; } int main(int argc, char **argv) { char buf[101]; CalendarTester<Gregorian, Julian> g; CalendarTester<Julian, Gregorian> j; g.set_companion(j); while(scanf("%100s", buf) == 1) { std::string command(buf); linenr++; if(command == "g") { g.handle(); } else if(command == "j") { j.handle(); } else { fprintf(stderr, "Got bad type: %s\n", command.c_str()); assert(!"Unknown type"); } } return 0; }

Dold text

Och här är calenderklassen som jag simplifierade till koden som jag skickade upp här på swec.

#ifndef CALENDAR_H_ #define CALENDAR_H_ // projektlokala headerfiler #include "kattistime.h" #include "gregorian.h" #include "event.h" // STL headerfiler #include <iostream> #include <assert.h> #include <ctime> #include <vector> #include <algorithm> namespace lab2 { template<class T> class Calendar { typedef typename std::vector<Event<T> *> EVector; typedef typename EVector::iterator EVectorIt; public: Calendar(); Calendar(const T &); ~Calendar(); Calendar(const Calendar &); Calendar<T> & operator=(const Calendar<T> &); bool set_date(int year, int month, int day); bool add_event(std::string event); bool add_event(std::string event, int day); bool add_event(std::string event, int day, int month); bool add_event(std::string event, int day, int month, int year); bool remove_event(std::string event); bool remove_event(std::string event, int day); bool remove_event(std::string event, int day, int month); bool remove_event(std::string event, int day, int month, int year); EVector events; T * date; friend std::ostream & operator<<(std::ostream & out, const Calendar & cal) { for (unsigned int i = 0; i < cal.events.size(); i++) { if (*(cal.events[i]->d) >= *(cal.date)) { out << *(cal.events[i]->d) << " : " << cal.events[i]->happening << "\n"; } } return out; } }; template<class T> Calendar<T>::Calendar() { date = new T(); } template<class T> Calendar<T>::Calendar(const T & in) : date(in) { } template<class T> Calendar<T>::Calendar(const Calendar<T> & calendar) { date = new T(calendar.date); for (unsigned int i = 0; i < calendar.events.size(); i++) { Event<T> * key = new Event<T>(*calendar.events[i]); events.push_back(key); } } template<class T> Calendar<T>& Calendar<T>::operator =(const Calendar<T> & calendar) { if (this == &calendar){ return *this; } delete date; date = new T(calendar.date); for (unsigned int i = 0; i < events.size(); i++) { delete events[i]; } events.empty(); for (unsigned int i = 0; i < calendar.events.size(); i++) { Event<T> * key = new Event<T>(*calendar.events[i]); events.push_back(key); } return *this; } template<class T> Calendar<T>::~Calendar() { delete &date; for (unsigned int i = 0; i < events.size(); i++) { delete events[i]; } events.empty(); } template<class T> bool Calendar<T>::set_date(int year, int month, int day) { try { T * t = new T(year, month, day); date = t; } catch (std::out_of_range) { return false; } return true; } template<class T> bool Calendar<T>::add_event(std::string happening) { return add_event(happening, date->day(), date->month(), date->year()); } template<class T> bool Calendar<T>::add_event(std::string happening, int day) { return add_event(happening, day, date->month(), date->year()); } template<class T> bool Calendar<T>::add_event(std::string happening, int day, int month) { return add_event(happening, day, month, date->year()); } template<class T> bool Calendar<T>::add_event(std::string happening, int day, int month, int year) { Event<T> * event = new Event<T>(happening, day, month, year); EVectorIt left, mid, right; left = events.begin(); right = events.end(); int count = distance(left, right); while (0 < count) { // divide and conquer to find lowest bound (ei first element >= key). int count2 = count / 2; mid = left; advance(mid, count2); if (*(*(mid)) < *event) { // try left half left = ++mid; count -= count2 + 1; } else { count = count2; } } // the specs says that list should be ordered by date first and insert order next. // this means we can't just sort and then add because we need walk over all with same dates and check for comparison and then add. // left points to first element not less than key (e.i same date) if (left != right) { // if we are not at the end while (*(*(*left)).d == *(*event).d) { // go over all with the same date if ((*(*left)) == *event) { // and check for identical key return false; // if same key is found, return false. } left++; if (left == right) { // if we came to the end events.push_back(event); // add and return return true; } } events.insert(left, event); // we have walked over all elements with same date and none were same as key and we are not at the end. return true; } else { // left is equals right, we are at the end. events.push_back(event); return true; } } template<class T> bool Calendar<T>::remove_event(std::string happening) { return remove_event(happening, date->day(), date->month(), date->year()); } template<class T> bool Calendar<T>::remove_event(std::string happening, int day) { return remove_event(happening, day, date->month(), date->year()); } template<class T> bool Calendar<T>::remove_event(std::string happening, int day, int month) { return remove_event(happening, day, month, date->year()); } template<class T> bool Calendar<T>::remove_event(std::string happening, int day, int month, int year) { Event<T> event(happening, day, month, year); EVectorIt left, mid, right; left = events.begin(); right = events.end(); int count = distance(left, right); while (0 < count) { // divide and conquer, find half that contains answer int count2 = count / 2; mid = left; advance(mid, count2); if (*(*(mid)) < event) { // try left half left = ++mid; count -= count2 + 1; } else if (*(*(mid)) == event) { events.erase(mid); return true; } else { count = count2; } } return false; } } #endif

Dold text

Och det kompletta felmedelandet jag får när jag kör koden:

g++ -O0 -g3 -pg -Wall -c -fmessage-length=0 -MMD -MP -MF"cprog09lab23.d" -MT"cprog09lab23.d" -o "cprog09lab23.o" "../cprog09lab23.cpp" ../cprog09lab23.cpp: In member function ‘void {anonymous}::CalendarTester<T, S>::handle() [with T="lab2::Gregorian," S="lab2::Julian"]’: ../cprog09lab23.cpp:175:13: instantiated from here ../cprog09lab23.cpp:71:6: error: no matching function for call to ‘lab2::Calendar<lab2::Gregorian>::Calendar(const lab2::Calendar<lab2::Julian>&)’ ../cprog09lab23.cpp:71:6: note: candidates are: ../calendar.h:68:19: note: lab2::Calendar<T>::Calendar(const lab2::Calendar<T>&) [with T="lab2::Gregorian"] ../calendar.h:68:19: note: no known conversion for argument 1 from ‘const lab2::Calendar<lab2::Julian>’ to ‘const lab2::Calendar<lab2::Gregorian>&’ ../calendar.h:63:19: note: lab2::Calendar<T>::Calendar(const T&) [with T="lab2::Gregorian"] ../calendar.h:63:19: note: no known conversion for argument 1 from ‘const lab2::Calendar<lab2::Julian>’ to ‘const lab2::Gregorian&’ ../calendar.h:59:19: note: lab2::Calendar<T>::Calendar() [with T="lab2::Gregorian"] ../calendar.h:59:19: note: candidate expects 0 arguments, 1 provided ../cprog09lab23.cpp:175:13: instantiated from here ../cprog09lab23.cpp:79:6: error: no match for ‘operator=’ in ‘*(({anonymous}::CalendarTester<lab2::Gregorian, lab2::Julian>*)this)->{anonymous}::CalendarTester<lab2::Gregorian, lab2::Julian>::cals.std::vector<_Tp, _Alloc>::operator[] [with _Tp = lab2::Calendar<lab2::Gregorian>*, _Alloc = std::allocator<lab2::Calendar<lab2::Gregorian>*>, std::vector<_Tp, _Alloc>::reference = lab2::Calendar<lab2::Gregorian>*&, std::vector<_Tp, _Alloc>::size_type = long unsigned int](to) = *(const lab2::Calendar<lab2::Julian>*)(({anonymous}::CalendarTester<lab2::Gregorian, lab2::Julian>*)this)->{anonymous}::CalendarTester<lab2::Gregorian, lab2::Julian>::others->std::vector<_Tp, _Alloc>::operator[] [with _Tp = lab2::Calendar<lab2::Julian>*, _Alloc = std::allocator<lab2::Calendar<lab2::Julian>*>, std::vector<_Tp, _Alloc>::reference = lab2::Calendar<lab2::Julian>*&, std::vector<_Tp, _Alloc>::size_type = long unsigned int](from)’ ../cprog09lab23.cpp:79:6: note: candidate is: ../calendar.h:78:32: note: lab2::Calendar<T>& lab2::Calendar<T>::operator=(const lab2::Calendar<T>&) [with T="lab2::Gregorian"] ../calendar.h:78:32: note: no known conversion for argument 1 from ‘const lab2::Calendar<lab2::Julian>’ to ‘const lab2::Calendar<lab2::Gregorian>&’ ../cprog09lab23.cpp: In member function ‘void {anonymous}::CalendarTester<T, S>::handle() [with T="lab2::Julian," S="lab2::Gregorian"]’: ../cprog09lab23.cpp:177:13: instantiated from here ../cprog09lab23.cpp:71:6: error: no matching function for call to ‘lab2::Calendar<lab2::Julian>::Calendar(const lab2::Calendar<lab2::Gregorian>&)’ ../cprog09lab23.cpp:71:6: note: candidates are: ../calendar.h:68:19: note: lab2::Calendar<T>::Calendar(const lab2::Calendar<T>&) [with T="lab2::Julian"] ../calendar.h:68:19: note: no known conversion for argument 1 from ‘const lab2::Calendar<lab2::Gregorian>’ to ‘const lab2::Calendar<lab2::Julian>&’ ../calendar.h:63:19: note: lab2::Calendar<T>::Calendar(const T&) [with T="lab2::Julian"] ../calendar.h:63:19: note: no known conversion for argument 1 from ‘const lab2::Calendar<lab2::Gregorian>’ to ‘const lab2::Julian&’ ../calendar.h:59:19: note: lab2::Calendar<T>::Calendar() [with T="lab2::Julian"] ../calendar.h:59:19: note: candidate expects 0 arguments, 1 provided ../cprog09lab23.cpp:177:13: instantiated from here ../cprog09lab23.cpp:79:6: error: no match for ‘operator=’ in ‘*(({anonymous}::CalendarTester<lab2::Julian, lab2::Gregorian>*)this)->{anonymous}::CalendarTester<lab2::Julian, lab2::Gregorian>::cals.std::vector<_Tp, _Alloc>::operator[] [with _Tp = lab2::Calendar<lab2::Julian>*, _Alloc = std::allocator<lab2::Calendar<lab2::Julian>*>, std::vector<_Tp, _Alloc>::reference = lab2::Calendar<lab2::Julian>*&, std::vector<_Tp, _Alloc>::size_type = long unsigned int](to) = *(const lab2::Calendar<lab2::Gregorian>*)(({anonymous}::CalendarTester<lab2::Julian, lab2::Gregorian>*)this)->{anonymous}::CalendarTester<lab2::Julian, lab2::Gregorian>::others->std::vector<_Tp, _Alloc>::operator[] [with _Tp = lab2::Calendar<lab2::Gregorian>*, _Alloc = std::allocator<lab2::Calendar<lab2::Gregorian>*>, std::vector<_Tp, _Alloc>::reference = lab2::Calendar<lab2::Gregorian>*&, std::vector<_Tp, _Alloc>::size_type = long unsigned int](from)’ ../cprog09lab23.cpp:79:6: note: candidate is: ../calendar.h:78:32: note: lab2::Calendar<T>& lab2::Calendar<T>::operator=(const lab2::Calendar<T>&) [with T="lab2::Julian"] ../calendar.h:78:32: note: no known conversion for argument 1 from ‘const lab2::Calendar<lab2::Gregorian>’ to ‘const lab2::Calendar<lab2::Julian>&’ make: *** [cprog09lab23.o] Error 1 **** Build Finished ****

Dold text

Om ni har ork och tid får ni mer än gärna ge feedback på min kod!

Nu ska jag sätta mig och försöka förstå vart "S" kommer ifrån i raden
Container(const Container<S> &in) : obj(new T(*static_cast<BaseClass*>(in.obj))) {};
samt varför template<typename T> istället för template<class T>

Tack alla för all hjälp! Det är underbart!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

som jag var inne på ovan så går det nog att få till med en copyctor typ Calendar(const Date&). i praktiken är det ingen skillnad mellan typename och class, men konsekvens är alltid trevligt.

cheers

Permalänk
Datavetare
Skrivet av Mikael_Berglund:

Nu ska jag sätta mig och försöka förstå vart "S" kommer ifrån i raden
Container(const Container<S> &in) : obj(new T(*static_cast<BaseClass*>(in.obj))) {};
samt varför template<typename T> istället för template<class T>

Tack alla för all hjälp! Det är underbart!

Sorry att jag ändrade "class" till "typename". När man införde templates i C++ så användes "class" men man insåg senare att det var ett ganska dåligt namn då man faktiskt inte behöver använda klasser utan argument till templates är normalt sett namn på en typ som man specificerar när man väl använder sin template. Så "typename" och "class" har i praktiken samma betydelse när de används i en template definition, själv använder jag uteslutande "typename" då jag anser det bättre beskriva vad det är.

template<typename T> class Container ... template<typename S> Container(const Container<S> &in) : obj(new T(*in.obj)) {}; };

Vad det gäller denna kod så definierar vi här en klass med en fri typ vi kallar T. Sedan definieras en av konstruktörerna med ÄNNU en fri typ, som vi kallar S.

Så varje instans av Container är av typen Container<T>, t.ex. Container<DerivedB>.
Denna klass innehåller bl.a. en pekare till ett objekt av typen DerivedB. I denna klass har vi sedan lämnat möjligheten att skapa en ctor som tar en typ S. Raden

Container(const Container<S> &in) : obj(new T(*in.obj)) {};

kommer ställa några implicita krav på typen S, bl.a. så måste en instans av S kunna användas som argument till ctor for typen T som är DerivedB i detta exempel. Tittar vi på vilka ctors som DerivedB har så inser vi då att kravet på S är att det, på något sätt, kan konverteras till typen BaseClass. Därför kan denna ctor används i ditt exempel då S kan vara DerivedA och det är den ENDA ctor som kan används eftersom den andra ctor som är defininerad tar ett objekt av typen DerivedB, vilket inte är vad vi använder i main.

Det man måste inse är att två instanser av template:en Container<T> är invariant men du vad du vill ha är en Container<T> som är covariant. Finns språk där man kan beskriva detta i definitionen av parameterized types (templates i C++), ett sådant är Scala. C++ har inte denna finess, utan man får göra en work-around genom att skapa en ctor som tar en typ till som argument, S i mitt exempel.

Det finns ställen där C++ stödjer covariant, ett sådant är värden man retunerar från en funktion. Funktionen kan retunera BaseClass och vi kan då utan problem retunera ett objekt av typen DerivedB, men typer skapade från en template är alltid invarianta.

Hoppas det "make some kind of sense"

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Skrivet av Yoshman:

... Some Awesomness! ...

Sorry för det sena svaret för så mycket AWESOME hjälp! Jag var lite för trött på att bråka med C++ så jag lade det på hyllan och fokuserade på roligare programmering ett tag (Java FTW!) men nu är jag tillbaka för att göra livet lite krångligare för alla er.

Jag har suttit och lekt lite med eran kod och funderat lite och tror jag förstår vad som händer, dock har jag stött på ett problem som jag inte riktigt förstår hur jag ska komma runt. Det problem jag får är när jag försöker applicera min testkod på min riktiga kod är att jag inte förstår hur jag ska hantera const och casting riktigt.

I mitt testexempel så hade jag bara ett litet testobjekt, men när jag istället har en array så får jag ett felmedelande som jag inte riktigt förstår hur jag ska ta mig runt.

template<class S> Calendar<T> & operator=(const Calendar<S> & in) { // for some reason i could not do "this == &in", just C++ fucking with me as usual i presume.. :/ const void* thisPointer = this; if (thisPointer == &in) { return *this; } delete date; date = new T(in.date); for (unsigned int i = 0; i < events.size(); i++) { delete events[i]; } events.empty(); for (unsigned int i = 0; i < in.events.size(); i++) { Event<T> * key = new Event<T > (*static_cast<Event<Date>*> (in.events[i])); // DENNA RAD ÄR FEL events.push_back(key); } return *this; }

Problemet jag får är:

calendar.h:76:90: error: invalid static_cast from type ‘lab2::Event<lab2::Julian>* const’ to type ‘lab2::Event<lab2::Date>*’

Ett event är någonting som håller en händelse (typ julafton) tillsammans med ett datumobjekt som antingen är baserat på Gregorian eller Julian (båda ärver av Date) så att kasta ifrån Julian eller Gregorian till Date "borde" funka, jag antar att min Julian är const för att copy-konstruktors argument är deklarerat som const och det är ju rimligt men jag kommer inte på hur jag ska göra för att komma runt det hela.

Istället för att klistra in en massa kod så kastade jag upp hela mitt netbeans-projekt på github:
https://github.com/PhroZenOne/calendar/blob/master/
speciellt event.h antar jag är intressant: https://github.com/PhroZenOne/calendar/blob/master/event.h

Evigt tacksam ifall någon stackare har tid att hjälpa mig lite!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

Du kan nog kopiera objektet först och sedan casta det, const skyddar original objektet från att ändras. Dvs du vill inte göra ändringar i den du kopierar ifrån utan du vill ändra det du sparar. Om jag förstår det rätt.

Visa signatur

orka