[C++] Fråga angående Template Class

Permalänk
Medlem

[C++] Fråga angående Template Class

Hej!

Har en fråga som inte riktigt besvarats av mina sökningar på Google som jag hoppas ni kan hjälpa mig med! Jag använder mig av en Template Class som ser ut ungefär såhär:

// template_class.h template <class T> class TempClass { private: ... public: TempClass(int size) { ... } };

Jag kan utan problem få objekt av denna Template Class under main på följande sätt:

// main.cpp #include <template_class.h> int main(int argc, char** argv) { ... TempClass<std::vector<int> > test(100); ... }

Jag försöker nu att skapa ett objekt av min Template Class inuti en annan Class men det verkar inte fungera. Har testat att göra bl.a. på följande sätt:

// some_other_class.h #include <template_class.h> class SomeOtherClass { private: TempClass<std::vector<int> > test(int k); public: SomeOtherClass(); ... };

// some_other_class.cpp SomeOtherClass::SomeOtherClass() { test(100); }

Vid kompilering får jag följande felmeddelande:

undefined reference to `SomeOtherClass::test(int)' collect2: error: ld returned 1 exit status

Vad gör jag för fel? Hur kan jag använda mig av ett objekt av en Template Class inuti en annan Class?

Tack för svar!

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII

Permalänk
Medlem

TempClass<std::vector<int> > test(int k);

Detta är en funktionsdeklaration för en funktion som tar en int och returnerar en TempClass<std::vector<int>>. Felet du får beror på att länkaren inte kan hitta någon definition på funktionen. Ta bort (int k) så ska du se att det fungerar bättre.

Permalänk
Medlem
Skrivet av perost:

TempClass<std::vector<int> > test(int k);

Detta är en funktionsdeklaration för en funktion som tar en int och returnerar en TempClass<std::vector<int>>. Felet du får beror på att länkaren inte kan hitta någon definition på funktionen. Ta bort (int k) så ska du se att det fungerar bättre.

Tar jag bort (int k) från funktionsdeklarationen och kompilerar därefter får jag istället följande felmeddelande:

error: no matching function for call to ‘SomeOtherClass::test(int)’ test(100);

Kan det bero på att jag deklarerar försöker sätta size = 100 på följande sätt i .cpp filen?

// some_other_class.cpp SomeOtherClass::SomeOtherClass() { test(100); }

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII

Permalänk
Medlem
Skrivet av Icte:

Tar jag bort int k från funktionsdeklarationen och kompilerar därefter får jag följande felmeddelande:

Är det en funktion eller ett objekt du försöker deklarera? Om det är en funktion så måste du definiera den någonstans. Om det är ett objekt så försöker du initialisera den fel:

SomeOtherClass::SomeOtherClass() : test(100) // Initialisering av objektet test, anropar konstruktorn för TempClass. { test(100); // Ett anrop till funktionen test. }

Permalänk
Medlem
Skrivet av perost:

Är det en funktion eller ett objekt du försöker deklarera? Om det är en funktion så måste du definiera den någonstans. Om det är ett objekt så försöker du initialisera den fel:

SomeOtherClass::SomeOtherClass() : test(100) // Initialisering av objektet test, anropar konstruktorn för TempClass. { test(100); // Ett anrop till funktionen test. }

Det är ett objekt av klassen TempClass som jag försöker deklarera. Denna innehåller egna funktioner som jag sedan vill använda mig av inuti klassen SomeOtherClass. Jag kan fortfarande deklarera objekt från andra klasser som ser ut på följande sätt:

//some_third_class.h class SomeThirdClass { private: public: SomeThirdClass() { } };

på följande sätt inuti SomeOtherClass:

// some_other_class.h #include <template_class.h> #include <some_third_class.h> class SomeOtherClass { private: //TempClass<std::vector<int> > test(int k); SomeThirdClass stc; public: SomeOtherClass(); void someFunction(); ... };

Därefter går det bra att använda mig av SomeThirdClass' funktioner på följande sätt

... stc.someFunction(); ...

Går det att använda Template Class på liknande sätt? D.v.s. går det att deklarera en Template Class inuti en Class för att bl.a. använda dess funktioner inuti den senare klassen?

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII

Permalänk
Medlem
Skrivet av Icte:

Går det att använda Template Class på liknande sätt? D.v.s. går det att deklarera en Template Class inuti en Class för att bl.a. använda dess funktioner inuti den senare klassen?

Ja, att det är en template-klass har ingenting med dina problem att göra, du hade fått precis samma problem om TempClass var en vanlig klass. Använd TempClass som vanligt bara, förutom att du använder TempClass<typ> när du deklarar objekt av klassen.

Permalänk
Legendarisk

@Icte: TempClass<std::vector<int> > test(int k); deklarerar en funktion, du behöver ändra det till TempClass<std::vector<int> > test;, och som @perost demonstrerade så bör du intialisera den antingen i konstruktorns initialiseringslista eller propertydeklarationen för att undvika en onödig defaultinitialisering.

// some_other_class.h class SomeOtherClass { private: TempClass<std::vector<int>> test; public: SomeOtherClass(); }; // some_other_class.cpp SomeOtherClass::SomeOtherClass() : test(100) { }

Visa signatur

Abstractions all the way down.

Permalänk
Medlem
Skrivet av perost:

Ja, att det är en template-klass har ingenting med dina problem att göra, du hade fått precis samma problem om TempClass var en vanlig klass. Använd TempClass som vanligt bara, förutom att du använder TempClass<typ> när du deklarar objekt av klassen.

Skrivet av Tunnelsork:

@Icte: TempClass<std::vector<int> > test(int k); deklarerar en funktion, du behöver ändra det till TempClass<std::vector<int> > test;, och som @perost demonstrerade så bör du intialisera den antingen i konstruktorns initialiseringslista eller propertydeklarationen för att undvika en onödig defaultinitialisering.

// some_other_class.h class SomeOtherClass { private: TempClass<std::vector<int>> test; public: SomeOtherClass(); }; // some_other_class.cpp SomeOtherClass::SomeOtherClass() : test(100) { }

Tack för svar! Jag antog att man skulle deklarera Template Class på samma sätt på en icke-Template Class men jag verkar ändå få problem vid deklarationen av denna. Deklarerar jag på följande sätt (d.v.s. precis som en vanlig, icke-Template Class) får jag följande felmeddelande vid kompilering (detta med [cmd]test(100);[cmd] bortkommenterat under SomeOtherClass.cpp):

TempClass<std::vector<int> > test;

error: no matching function for call to ‘TempClass<std::vector<int> >::TempClass()’ SomeOtherClass::SomeOtherClass() {

Om jag istället kompilerar med TempClass<std::vector<int> > test(); och med detta med [cmd]test(100);[cmd] bortkommenterat under SomeOtherClass.cpp så kompilerar det utan problem. TempClass fungerar för övrigt under main precis som jag skrivit den ovan och programmet kör som det ska göra.

Kan problemet ligga iom att jag använder mig av C++98 istället för C++11 ?

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII

Permalänk
Medlem
Skrivet av Icte:

Om jag istället kompilerar med TempClass<std::vector<int> > test(); och med detta med [cmd]test(100);[cmd] bortkommenterat under SomeOtherClass.cpp så kompilerar det utan problem.

Ja, då har du deklarerat en funktion som heter test. Eftersom du aldrig anropar funktionen så spelar det ingen roll att du inte definierat funktionen någonstans. Den raden har alltså ingen som helst funktion.

Skrivet av Icte:

Kan problemet ligga iom att jag använder mig av C++98 istället för C++11 ?

Nej, problemet ligger i att du måste initialisera dina objekt korrekt. Här kan du t.ex. läsa om hur det fungerar.

Tillägg: Fast du kanske ska börja med lektionen före förresten, eftersom du inte riktigt verkar förstå hur default-konstruktorer fungerar.

Permalänk
Legendarisk

@Icte: Anledningen till att det kompilerar med parenteser är att det då parsas som en funktionsdeklaration (som aldrig används), och anledningen till att det *inte* kompilerar utan parenteser och initalisering är att defaultinitialiseringen misslyckas eftersom att TempClass inte har någon defaultkonstruktor.

Visa signatur

Abstractions all the way down.

Permalänk
Medlem
Skrivet av perost:

Ja, då har du deklarerat en funktion som heter test. Eftersom du aldrig anropar funktionen så spelar det ingen roll att du inte definierat funktionen någonstans. Den raden har alltså ingen som helst funktion.
Nej, problemet ligger i att du måste initialisera dina objekt korrekt. Här kan du t.ex. läsa om hur det fungerar.

Tillägg: Fast du kanske ska börja med lektionen före förresten, eftersom du inte riktigt verkar förstå hur default-konstruktorer fungerar.

Skrivet av Tunnelsork:

@Icte: Anledningen till att det kompilerar med parenteser är att det då parsas som en funktionsdeklaration (som aldrig används), och anledningen till att det *inte* kompilerar utan parenteser och initalisering är att defaultinitialiseringen misslyckas eftersom att TempClass inte har någon defaultkonstruktor.

Jag tror att jag börjar förstå problemet:

För att kunna deklarera ett objekt av en klass vid namn TempClass (och ha tillgång till dess funktioner) inuti en annan klass vid namn SomeOtherClass så måste TempClass ha en default constructor, d.v.s:

// template_class.h template <class T> class TempClass { private: ... public: TempClass() { // take no parameters here! ... } };

Stämmer detta?

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII

Permalänk
Medlem
Skrivet av Icte:

För att kunna deklarera ett objekt av en klass vid namn TempClass (och ha tillgång till dess funktioner) inuti en annan klass vid namn SomeOtherClass så måste TempClass ha en default constructor, d.v.s:

Nej, inte riktigt. Du kan fortfarande använda en annan konstruktor än default-konstruktorn om du initialiserar objektet själv istället för att låta kompilatorn sköta det.

Permalänk
Medlem
Skrivet av perost:

Nej, inte riktigt. Du kan fortfarande använda en annan konstruktor än default-konstruktorn om du initialiserar objektet själv istället för att låta kompilatorn sköta det.

Då hänger jag med! Har löst problemet genom att använda default construktor denna gång då det verkade vara det lättaste tillgångasättet för mitt problem.

Tack för hjälpen!

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII

Permalänk
Legendarisk
Skrivet av Icte:

Då hänger jag med! Har löst problemet genom att använda default construktor denna gång då det verkade vara det lättaste tillgångasättet för mitt problem.

Jag kanske missförstår, men om du menar att du lade till en default-konstruktor på TempClass<T> och sedan återinitialiserar den inuti SomeOtherClass::SomeOtherClass() så innebär det att det först initialiseras en TempClass<T> (och att alla dess properties initialiseras...) och att de sedan ersätts med ett annat värde. Du kan undvika det genom att använda initialiseringslistan.

#include <iostream> using namespace std; class member { public: member() { cout << "member::member()" << endl; } ~member() { cout << "member::~member()" << endl; } }; class foo { private: member a; member b; member c; public: foo() { cout << "foo::foo()" << endl; } ~foo() { cout << "foo::~foo()" << endl; } foo(int n) { cout << "foo::foo(int n)" << endl; } foo& operator=(const foo& rhs) { cout << "foo& operator=(const foo& rhs)" << endl; return *this; } }; class bar { private: foo f; public: bar() { f = foo(123); } bar(bool) : f(123) { } }; int main() { bar a; cout << endl << " --- " << endl << endl; bar b (true); cout << endl << " --- " << endl << endl; }

Exempel

# Via automatisk initialisering + manuell tilldelning: member::member() # Initialisering av properties i foo: member::member() # ... member::member() # ... foo::foo() # Automatiskt anrop till foo's default-konstruktor # (Orsakade ovanstående rader) member::member() # Intialisering av properties i foo: member::member() # ... member::member() # ... foo::foo(int n) # Manuell initialisering av foo # (Orsakade ovanstående rader) foo& operator=(const foo& rhs) # Copy assignment till bar::f foo::~foo() # ... innebär även att den överblivna foo städas upp member::~member() # ... tillsammans med allt sitt innehåll member::~member() # ... member::~member() # ... # Via initialiseringslista: member::member() # Initialisering av properties i foo: member::member() # ... member::member() # ... foo::foo(int n) # Direkt initialisering med rätt konstruktor

Att använda sig av en initaliseringslista kan alltså bespara dig en hel del arbete, det är mindre som kan gå fel eftersom att du bara behöver initialisera objektet en gång, och om du inte avser att TempClass<T> egentligen ska kunna initialiseras utan argument så undviker du att de som använder klassen kan göra det av misstag.

Visa signatur

Abstractions all the way down.

Permalänk
Medlem
Skrivet av Tunnelsork:

Jag kanske missförstår, men om du menar att du lade till en default-konstruktor på TempClass<T> och sedan återinitialiserar den inuti SomeOtherClass::SomeOtherClass() så innebär det att det först initialiseras en TempClass<T> (och att alla dess properties initialiseras...) och att de sedan ersätts med ett annat värde. Du kan undvika det genom att använda initialiseringslistan.

#include <iostream> using namespace std; class member { public: member() { cout << "member::member()" << endl; } ~member() { cout << "member::~member()" << endl; } }; class foo { private: member a; member b; member c; public: foo() { cout << "foo::foo()" << endl; } ~foo() { cout << "foo::~foo()" << endl; } foo(int n) { cout << "foo::foo(int n)" << endl; } foo& operator=(const foo& rhs) { cout << "foo& operator=(const foo& rhs)" << endl; return *this; } }; class bar { private: foo f; public: bar() { f = foo(123); } bar(bool) : f(123) { } }; int main() { bar a; cout << endl << " --- " << endl << endl; bar b (true); cout << endl << " --- " << endl << endl; }

Exempel

# Via automatisk initialisering + manuell tilldelning: member::member() # Initialisering av properties i foo: member::member() # ... member::member() # ... foo::foo() # Automatiskt anrop till foo's default-konstruktor # (Orsakade ovanstående rader) member::member() # Intialisering av properties i foo: member::member() # ... member::member() # ... foo::foo(int n) # Manuell initialisering av foo # (Orsakade ovanstående rader) foo& operator=(const foo& rhs) # Copy assignment till bar::f foo::~foo() # ... innebär även att den överblivna foo städas upp member::~member() # ... tillsammans med allt sitt innehåll member::~member() # ... member::~member() # ... # Via initialiseringslista: member::member() # Initialisering av properties i foo: member::member() # ... member::member() # ... foo::foo(int n) # Direkt initialisering med rätt konstruktor

Att använda sig av en initaliseringslista kan alltså bespara dig en hel del arbete, det är mindre som kan gå fel eftersom att du bara behöver initialisera objektet en gång, och om du inte avser att TempClass<T> egentligen ska kunna initialiseras utan argument så undviker du att de som använder klassen kan göra det av misstag.

Nej det jag gjorde var att jag tog bort parametern size från konstruktorn samt alla variabler därifrån och skapade en funktion void setSize(int size) (som innehar allt som fanns inuti den tidigare konstruktorn) som jag istället kommer att använda mig av innan jag kan använda de andra funktionerna i TempClass. Detta har än så länge inte påverkat mitt program på något negativt sätt (har inte märkt av några buggar så länge som setSize körs innan någon av de andra funktionerna och det fungerar precis som tänkt). Initialiseringslista är nytt för mig, ska definitivt kika på detta!

Visa signatur

12c/24t 4.0GHz (Zen2) • 2x16GiB 3200MHz C14 • RTX 2080 FE 1965MHz 7000MHz • X570 I PW • Ghost S1 MKII