Szablon klasy, w plikach *.hpp i *.cpp

0

Mam problem szablonem klasy gdy rozbijam go na dwa pliki, nagłówkowy i źródłowy. Przy kompilacji twierdzi że nie ma referencji do metod. Gdy ten sam kod jest w jednym pliku nie ma problemu z kompilacją i działaniem. Szukam rozwiązania problemu na przykładowym kodzie pokazany poniżej. Jeżeli ta sama klasę zdefiniuje bez szablonu kompilacja przebiega bez problemu.

//main.cpp
#include <iostream>
#include "nowaKlasa.hpp"

int main()
{
    dane<bool,3> A("bool",0,1);
    dane<float,5> B("float",11.32,0.0101);
    dane<int,8> C("int",5,2);
    A.drukuj();
    B.drukuj();
    C.drukuj();
}

//nowaKlasa.hpp
#ifndef KLASA_HPP
#define KLASA_HPP
#include <iostream>

using namespace std;
template<typename liczba, int size>
class dane
{
public:
    dane(string,liczba,liczba);
    ~dane();
    void drukuj();
private:
    liczba x,y;
    liczba *tab;
    string nazwa;
};
#endif


//nowaKlasa.cpp
#include "nowaKlasa.hpp"
template<typename liczba, int size>
dane<liczba, size>::~dane()
{
    delete [] tab;
    cout << "Pamięć<" << this << ">wyczyszczona!" << endl;
}
template<typename liczba, int size>
void dane<liczba,size>::drukuj()
{
    cout.setf(ios_base::boolalpha);
    cout << nazwa << "(" << x << "," << y << ")" << endl;
    cout << "tab: ";
    for(int i=0;i<size;i++)
        cout << tab[i] << " ";
    cout << endl;
}
template<typename liczba, int size>
dane<liczba,size>::dane(string nazwa,liczba x, liczba y)
{
    this->x=x;
    this->y=y;
    this->nazwa=nazwa;
    tab = new liczba[size];
}

W czym może być problem? Sposobie wywołania kompilatora? Nie mam pojęcia.

2

Musisz umieścić implementację metod klasy szablonowej w pliku nagłówkowym.

//nowaKlasa.hpp
#ifndef KLASA_HPP
#define KLASA_HPP
#include <iostream>

using namespace std;
template<typename liczba, int size>
class dane
{
public:
    dane(string,liczba,liczba);
    ~dane();
    void drukuj();
private:
    liczba x,y;
    liczba *tab;
    string nazwa;
};

template<typename liczba, int size>
dane<liczba, size>::~dane()
{
    delete [] tab;
    cout << "Pamięć<" << this << ">wyczyszczona!" << endl;
}
template<typename liczba, int size>
void dane<liczba,size>::drukuj()
{
    cout.setf(ios_base::boolalpha);
    cout << nazwa << "(" << x << "," << y << ")" << endl;
    cout << "tab: ";
    for(int i=0;i<size;i++)
        cout << tab[i] << " ";
    cout << endl;
}
template<typename liczba, int size>
dane<liczba,size>::dane(string nazwa,liczba x, liczba y)
{
    this->x=x;
    this->y=y;
    this->nazwa=nazwa;
    tab = new liczba[size];
}

#endif

//nowaKlasa.cpp
#include "nowaKlasa.hpp"

//main.cpp
#include <iostream>
#include "nowaKlasa.hpp"

int main()
{
    dane<bool,3> A("bool",0,1);
    dane<float,5> B("float",11.32,0.0101);
    dane<int,8> C("int",5,2);
    A.drukuj();
    B.drukuj();
    C.drukuj();
}

Ewentualnie, jeśli chcesz, to możesz zrobić jawną instancję klasy szablonowej w nowaKlasa.cpp, ale musisz wtedy wiedzieć z góry jakie typy wejdą w przyszłości do parametrów szablonu.
Możesz to osiągnąć przez twój kod + dodanie na końcu nowaKlasa.cpp:

template class dane<bool,3>;
template class dane<float,5>;
template class dane<int,8>;

Obstawiam, że to rozwiązanie nie ma sensu w twoim przypadku.

0
mwl4 napisał(a):

Musisz umieścić implementację metod klasy szablonowej w pliku nagłówkowym.

//nowaKlasa.hpp
#ifndef KLASA_HPP
#define KLASA_HPP
#include <iostream>

using namespace std;
template<typename liczba, int size>
class dane
{
public:
    dane(string,liczba,liczba);
    ~dane();
    void drukuj();
private:
    liczba x,y;
    liczba *tab;
    string nazwa;
};

template<typename liczba, int size>
dane<liczba, size>::~dane()
{
    delete [] tab;
    cout << "Pamięć<" << this << ">wyczyszczona!" << endl;
}
template<typename liczba, int size>
void dane<liczba,size>::drukuj()
{
    cout.setf(ios_base::boolalpha);
    cout << nazwa << "(" << x << "," << y << ")" << endl;
    cout << "tab: ";
    for(int i=0;i<size;i++)
        cout << tab[i] << " ";
    cout << endl;
}
template<typename liczba, int size>
dane<liczba,size>::dane(string nazwa,liczba x, liczba y)
{
    this->x=x;
    this->y=y;
    this->nazwa=nazwa;
    tab = new liczba[size];
}

#endif

//nowaKlasa.cpp
#include "nowaKlasa.hpp"

//main.cpp
#include <iostream>
#include "nowaKlasa.hpp"

int main()
{
    dane<bool,3> A("bool",0,1);
    dane<float,5> B("float",11.32,0.0101);
    dane<int,8> C("int",5,2);
    A.drukuj();
    B.drukuj();
    C.drukuj();
}

Ewentualnie, jeśli chcesz, to możesz zrobić jawną instancję klasy szablonowej w nowaKlasa.cpp, ale musisz wtedy wiedzieć z góry jakie typy wejdą w przyszłości do parametrów szablonu.
Możesz to osiągnąć przez twój kod + dodanie na końcu nowaKlasa.cpp:

template class dane<bool,3>;
template class dane<float,5>;
template class dane<int,8>;

Obstawiam, że to rozwiązanie nie ma sensu w twoim przypadku.

Dziękuje za odpowiedź, to pierwsze rozwiązanie sprawdzałem i oczywiście działało.

0

@MichalLanczont:

Dla naswietlenia kontekstu, koncepcja include <plik> jest "bardzo nowoczesna", pochodzi w lat 196x, pacjent umarł 1980/90 ze śmiercią dużych projektow asseblerowych, a mumia jest konwerwowana jak ta na Palcu Czerwonym w C/C++/PHP *)

Już się rozjaśniło, dlaczego C++ #include jest takim super toolem do programowania ?

*) przy czym w PHP już jest obsolete, a znalazła się w języku przez kopiowanie bez zrozumienia klamerek i include z C i totalnie niezrozumianego dolara $ z Perla. Takie "Projektowanie Sterowane Kompleksami"

0

Już się rozjaśniło, dlaczego C++ #include jest takim super toolem do programowania ?

No, ale co tutaj #include winny? Na problem składają dwie podstawowe sprawy - czym są szablony jako takie oraz to, co tak na prawdę jest kompilowane przez kompilator. #include to tylko produkt uboczny ważniejszych mechanik w języku, których zrozumienie faktycznie daje odpowiedź dlaczego nie można "definiować" szablonów w plikach .cpp.

2
several napisał(a):

Już się rozjaśniło, dlaczego C++ #include jest takim super toolem do programowania ?

No, ale co tutaj #include winny? Na problem składają dwie podstawowe sprawy - czym są szablony jako takie oraz to, co tak na prawdę jest kompilowane przez kompilator. #include to tylko produkt uboczny ważniejszych mechanik w języku, których zrozumienie faktycznie daje odpowiedź dlaczego nie można "definiować" szablonów w plikach .cpp.

#include jest o tyle winny, że gdyby go nie było to ktoś zaimplementował rozwiązanie, które pozwala na udostępnianie bytów pomiędzy jednostkami kompilacji bez mozolnego utrzymywania headerów i bez podziału na funkcje/całą reszta

1
several napisał(a):

No, ale co tutaj #include winny?

Co winny ... to, że adepci języków z "importem" w ogóle nie stawiają tego pytania. Zero, null...

5-30% paliwa sie przepala w C, tym bardziej w C++ na nic, wyższy stopień wiedzy magicznej, guardy, czary. #include i #define
O wiele trudniejsze zadania stawia się przed IDE itd

0
mwl4 napisał(a):

Ewentualnie, jeśli chcesz, to możesz zrobić jawną instancję klasy szablonowej w nowaKlasa.cpp, ale musisz wtedy wiedzieć z góry jakie typy wejdą w przyszłości do parametrów szablonu.
Możesz to osiągnąć przez twój kod + dodanie na końcu nowaKlasa.cpp:

template class dane<bool,3>;
template class dane<float,5>;
template class dane<int,8>;

Dialekty sprzed 15-20 lat (Borlandy / command liniowe MS) "jakoś" sobie radziły z templejtami w CPP, ale
a) stawały wobec swoich własnych fabrycznych wydumek a nie standardowego jezyka
b) nawet się nie odniosę, czy ówczesne C++ miało jakiś numerek wersji. Borland nawet nie próbował starać się o kompatybilność ze standardem, a już bardziej mix-operacyjnosć z Pascalem, w tym straszliwie patchowany kompialator (błąd w obrębie klas kompatybilnych z Delphi wyglądał ogromnie inaczej, niż w reszcie kodu)

0
slsy napisał(a):

#include jest o tyle winny, że gdyby go nie było to ktoś zaimplementował rozwiązanie, które pozwala na udostępnianie bytów pomiędzy jednostkami kompilacji bez mozolnego utrzymywania headerów i bez podziału na funkcje/całą reszta

Czyli najpierw był #include a potem wokół niego projektowali język? Nonsens. #include to tylko rezultat wcześniejszych założeń i projektu języka. Jakby projekt technologii wyglądał inaczej to inaczej by wyglądał #include i to projekt technologii jest winny występującym problemom a nie narzędzie, które powstało by projekt zrealizować.

0
several napisał(a):

Czyli najpierw był #include a potem wokół niego projektowali język? Nonsens. #include to tylko rezultat wcześniejszych założeń i projektu języka. Jakby projekt technologii wyglądał inaczej to inaczej by wyglądał #include i to projekt technologii jest winny występującym problemom a nie narzędzie, które powstało by projekt zrealizować.

#include był ok rozwiązaniem pośrednim na początku tj. bardzo prostym do zaimplementowania i działającym jako tako . W latach 70 projekty nie były tak duże i skomplikowane. Ficzery mocno uwydadniające ten problem tj. pchanie coraz więcej i więcej kodu do headerów nie istniały. Lata mijały i problem powoli stawał się coraz bardziej dotkliwy, niestety przez istnienie gotowego rozwiązania nie dostaliśmy porządnego rozwiązania. Problem jest szczególnie widoczny w C++, ale największa zaleta C++ to właśnie kompatybilność z C, więc trochę by design mamy to co mamy

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