Program w paru plikach, szablony i prototyp

0

Uczę się pisać program w paru plikach (oraz jakieś początki z przestrzeniami, dlatego tu jest ich tyle, więc nie nie piszcie proszę, że to błąd, bo to tylko dla potrzeb nauki tylu użyłem ;) ) i natrafiłem na taki problem: "unresolved externals". Dopóki wszystkie funkcje przyjmowały i zwracały double, nie było problemu. Chciałem potem uogólnić program za pomocą szablonów, lecz z tego co wyczytałem coś jest nie tak z deklaracjami funkcji. Program prosty, ma liczyć pierwiastki równania kwadratowego. Visual C++ 2013

plik główny .cpp :

#include <iostream>
#include "definicje.h"

int main()
{
	double a, b, c, delta;
	wczytaj::wczytajabc(a, b, c);
	delta = delta::policz(a, b, c);
	wynik::policz(a, b, delta);
	std::cin.get();
	std::cin.get();
	return 0;
} 

funkcje.cpp:

#include <iostream>
#include <cmath>
#include "definicje.h"

namespace wczytaj
{
	template <typename T>
	void wczytajabc(T &a, T &b, T &c)
	{
		std::cout << "Podaj a,b,c: " << std::endl;
		std::cin >> a >> b >> c;
	}
}

namespace delta
{
	template <typename T>
	T policz(T a, T b, T c)
	{
		T delta = b*b - 4 * a*c;
		return delta;
	}
}

namespace wynik
{
	template <typename T>
	void policz(T a, T b, T delta)
	{
		if (delta < 0)
			wyswietl::wyswietlm();
		else if (delta == 0)
		{
			T x = (-b) / (2 * a);
			wyswietl::wyswietlr(x);
		}
		else
		{
			T x1, x2;
			x1 = (-b - sqrt(delta)) / (2 * a);
			x2 = (-b + sqrt(delta)) / (2 * a);
			wyswietl::wyswietlw(x1, x2);
		}
	}
}

namespace wyswietl
{
	void wyswietlm()
	{
		std::cout << "Delta mniejsza od 0, brak rozwiazan";
	}

	template <typename T>
	void wyswietlr(T x)
	{
		std::cout << "Delta rowna 0, wynik rownania to: " << x << std::endl;
	}

	template <typename T>
	void wyswietlw(T x1, T x2)
	{
		std::cout << "Delta wieksza od 0, rozwiazania rownania: " << std::endl << x1 << std::endl << x2 << std::endl;
	}
}
 

definicje.h (tu jest pewnie coś nie tak)

 #include <iostream>

namespace wczytaj
{
	template <typename T>
	void wczytajabc(T &a, T &b, T &c);
}

namespace delta
{
	template <typename T>
	T policz(T a, T b, T c);
}

namespace wynik
{
	template <typename T>
	void policz(T a, T b, T delta);
}

namespace wyswietl
{
	void wyswietlm();
	template <typename T>
	void wyswietlr(T x);
	template <typename T>
	void wyswietlw(T x1, T x2);
}

i błędy:

 error LNK2019: unresolved external symbol "void __cdecl wczytaj::wczytajabc<double>(double &,double &,double &)" (??$wczytajabc@N@wczytaj@@YAXAAN00@Z) referenced in function _main	
error LNK2019: unresolved external symbol "double __cdecl delta::policz<double>(double,double,double)" (??$policz@N@delta@@YANNNN@Z) referenced in function _main	
error LNK2019: unresolved external symbol "void __cdecl wynik::policz<double>(double,double,double)" (??$policz@N@wynik@@YAXNNN@Z) referenced in function _main	 
1

Definicje funkcji szablonowych muszą znajdować się w pliku nagłówkowym.

Załóżmy, że masz w projekcie 2 pliki foo.h i foo.cpp.

// foo.h
#ifndef FOO_H
#define FOO_H
template <typename T>
class Foo
{
  public:
    void setValue(T value);
    T getValue() const;
  private:
    T m_value;
};
#endif /* FOO_H */

// foo.cpp
#include "foo.h"

template <typename T>
void Foo<T>::setValue( T value)
{
   m_value = value;
}
template <typename T>
T Foo<T>::getValue() const
{
    return m_value;
}

Kolejno przypuśćmy, że w mainie masz coś takiego:

 
Foo<int> foo;
foo.setValue(5);
int value = foo.getValue();

Kompilator napotykając taki zapis tworzy nową klasę (konkretyzację szablonu klasy dla int). Tym niemniej żeby taki zabieg przebiegł pomyślnie kompilator musi mieć dostęp do implementacji takiego szablonu. Jeśli umieścisz implementację metod szablonu klasy w pliku *cpp (foo.cpp) to kompilator ich nie odnajdzie. Kompilacja jednak się powiedzie, ponieważ kompilator zakłada wtedy, że implementacja tych metod znajduje się w innym miejscu ("niech linker sobie sam poszuka tych definicji"). Definicji jak nie było, tak nadal nie ma, stąd linker krzyczy "undefined reference to ...".

Jest kilka rozwiązań tego problemu:

  1. Dołączenie pliku *.cpp (foo.cpp) na koniec pliku nagłówkowego.
// foo.h
#ifndef FOO_H
#define FOO_H
template <typename T>
class Foo
{
  public:
    void setValue(T value);
    T getValue() const;
  private:
    T m_value;
};

#include "foo.cpp"
#endif /* FOO_H */

// foo.cpp
#include "foo.h"

template <typename T>
void Foo<T>::setValue( T value)
{
   m_value = value;
}
template <typename T>
T Foo<T>::getValue() const
{
    return m_value;
}
  1. Explicit instantiation w *.cpp
// foo.cpp
#include "foo.h"

template <typename T>
void Foo<T>::setValue( T value)
{
   m_value = value;
}
template <typename T>
T Foo<T>::getValue() const
{
    return m_value;
}

template class Foo<int>;
  1. Definicja w *h.

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