Vinnaren i pepparkakshustävlingen!
2017-05-16, 11:22
  #1
Medlem
karatekungens avatar
Försöker inherit en klass Country utan template som base, till en annan klass Scandinavia som har template<class T>. Får ett konstigt felmeddelande som inte hjälper mig lösa problemet. Har provat hitta på nätet, men hittade inget som gjorde att man kunde förstå.

Någon som vet vad som är fel?

Felmeddelende (Visual Studio)
Citat:
Severity Code Description Project File Line Suppression State
Error LNK2005 "public: __thiscall Country::Country(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0Country@@QAE@V?$basic_string@DU?$char_traits@D @std@@V?$allocator@D@2@@std@@@Z) already defined in main.obj Kuk C:\Users\******\Desktop\Kuk\Kuk\Kuk\test.obj 1

.h-fil
Kod:
#pragma once
#include <string>
#include <iostream>
#include <cstdlib>
using namespace std;

 class Country {
private:
	string name;
public:
	Country();
	Country(string s);
	string getName() const;
	void setName(string s);
	virtual void printClass();
};

template <class T>
class Scandinavia : public Country {

private: 
	T population;
public:
	Scandinavia();
	Scandinavia(T p);
	Scandinavia(string n, T p);
	Scandinavia(string n);
	void printClass();
};

.cpp-fil
Kod:
#include "test.h"

void Country::printClass() {
	cout << "Country class. Name: " << getName() << endl;
}

Country::Country() { setName("Unknown"); }

Country::Country(string s) { setName(s); }
string Country::getName() const {
	return name;
}
void Country::setName(string s) {
	name = s;
}


template <class T>
Scandinavia<T>::Scandinavia() :Country() { population = NULL; }

template <class T>
Scandinavia<T>::Scandinavia(T p) { population = p; }

template <class T>
Scandinavia<T>::Scandinavia(string n) : Country(n) { population = NULL; }

template <class T>
Scandinavia<T>::Scandinavia(string n, T p) : Country(n) { population = p; }

template <class T>
void Scandinavia<T>::printClass() {
	cout << "Scandinavia class. Name: " << getName() << " Population: " << population << endl;
}

main.cpp-fil
Kod:
#include "test.h"

int main() {

	Scandinavia <int> Sverige("Sweden");
	Sverige.printClass();

	system("PAUSE");
	return 0;
}
__________________
Senast redigerad av karatekungen 2017-05-16 kl. 11:29.
Citera
2017-05-16, 13:50
  #2
Medlem
kaks avatar
Citat:
Ursprungligen postat av karatekungen
Försöker inherit en klass Country utan template som base, till en annan klass Scandinavia som har template<class T>. Får ett konstigt felmeddelande som inte hjälper mig lösa problemet. Har provat hitta på nätet, men hittade inget som gjorde att man kunde förstå.

Någon som vet vad som är fel?

Felmeddelende (Visual Studio)


.h-fil
Kod:
#pragma once
#include <string>
#include <iostream>
#include <cstdlib>
using namespace std;

 class Country {
private:
	string name;
public:
	Country();
	Country(string s);
	string getName() const;
	void setName(string s);
	virtual void printClass();
};

template <class T>
class Scandinavia : public Country {

private: 
	T population;
public:
	Scandinavia();
	Scandinavia(T p);
	Scandinavia(string n, T p);
	Scandinavia(string n);
	void printClass();
};

.cpp-fil
Kod:
#include "test.h"

void Country::printClass() {
	cout << "Country class. Name: " << getName() << endl;
}

Country::Country() { setName("Unknown"); }

Country::Country(string s) { setName(s); }
string Country::getName() const {
	return name;
}
void Country::setName(string s) {
	name = s;
}


template <class T>
Scandinavia<T>::Scandinavia() :Country() { population = NULL; }

template <class T>
Scandinavia<T>::Scandinavia(T p) { population = p; }

template <class T>
Scandinavia<T>::Scandinavia(string n) : Country(n) { population = NULL; }

template <class T>
Scandinavia<T>::Scandinavia(string n, T p) : Country(n) { population = p; }

template <class T>
void Scandinavia<T>::printClass() {
	cout << "Scandinavia class. Name: " << getName() << " Population: " << population << endl;
}

main.cpp-fil
Kod:
#include "test.h"

int main() {

	Scandinavia <int> Sverige("Sweden");
	Sverige.printClass();

	system("PAUSE");
	return 0;
}
Har inte provat i Visual Studio, men du har måste instantiera Scandinavia<int> någon stans där metod-definitionerna är synliga.
Alternativ 1: Flytta definitionerna till header-filen.
Alternativ 2: Instantiera explicit i cpp-filen "template class Scandinavia<int>;"

Alternativ 2 har naturligtvis nackdelen att du måste känna till alla tänkbara typer T som någon kan vilja använda.

Använd inte NULL. För det första är det inte C++, där använder du nullptr istället. Dessutom är det ju T inte en pekare. Sätt default värdet med "uniform initialization" syntax istället. Sen kan du ju använda default parametrar för att minska antalet konstruktorer som behövs.
Kod:
template<typename T>
class 
Scandinavia {
  
T population {}
  ... 

Till slut: Föredra alltid initialisering framför tilldelning.

Obs: Jag har inte testat detta i Visual Studio.
__________________
Senast redigerad av kak 2017-05-16 kl. 13:52.
Citera
2017-05-16, 16:21
  #3
Medlem
enowens avatar
Gjorde rent din kod lite, precis som kek säger så behöver du en instantiera din template klass. Flytta implementation till din header fil istället.

main.cpp

Kod:
#include "test.hpp"

int main()
{
    
Scandinavia<intsverige("hejsan"120);
    
sverige.printClass();


test.hpp

Kod:
#ifndef TEST_HPP
#define TEST_HPP

#include <iostream>
#include <string>

class Country
{
    protected:
        
std::string m_name;

    public:
        
Country();
        
Country(std::string name);
        
std::string getName() const;
        
void setName(std::string name);
        
virtual void printClass();
        
virtual ~Country() = default;
};

template <typename T>
class 
Scandinavia : public Country
{
    private:
        
T m_population;

    public:
        
Scandinavia();
        
Scandinavia(T population);
        
Scandinavia(std::string nameT population);
        
Scandinavia(std::string name);
        
void printClass() override;
        ~
Scandinavia() = default;
};

#endif 

test.cpp

Kod:
#include "test.hpp"

Country::Country() :
    
m_name{"Unknown"}
{}

Country::Country(std::string name) :
    
m_name{name}
{}

void Country::printClass()
{
    
std::cout << "Country class. Name: " << this->getName() << std::endl;
}

std::string Country::getName() const
{
    return 
this->m_name;
}

void Country::setName(std::string name)
{
    
this->m_name name;
}

template <typename T>
Scandinavia<T>::Scandinavia() :
    
Country(),
    
m_population{}
{}

template <typename T>
Scandinavia<T>::Scandinavia(T population) :
    
Country(),
    
m_population{population}
{}

template <typename T>
Scandinavia<T>::Scandinavia(std::string name) :
    
Country(name),
    
m_population{}
{}

template <typename T>
Scandinavia<T>::Scandinavia(std::string nameT population) :
    
Country(name),
    
m_population{population}
{}

template <typename T>
void Scandinavia<T>::printClass()
{
    
std::cout << "Scandinavia class. Name: " << this->getName() << " Population: " << this->m_population << std::endl;
}

template class Scandinavia<int>; 
Citera
2017-05-16, 17:37
  #4
Medlem
karatekungens avatar
Citat:
Ursprungligen postat av kak
...

Citat:
Ursprungligen postat av enowen
...
Tack båda två! Det fungerar nu.
Citera
2017-08-09, 04:40
  #5
Medlem
Citat:
Ursprungligen postat av karatekungen
Försöker inherit en klass Country utan template som base, till en annan klass Scandinavia som har template<class T>. Får ett konstigt felmeddelande som inte hjälper mig lösa problemet. Har provat hitta på nätet, men hittade inget som gjorde att man kunde förstå.

Någon som vet vad som är fel?

Felmeddelende (Visual Studio)

Severity Code Description Project File Line Suppression State
Error LNK2005 "public: __thiscall Country::Country(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??0Country@@QAE@V?$basic_string@DU?$char_traits@D @std@@V?$allocator@D@2@@std@@@Z) already defined in main.obj Kuk C:\Users\******\Desktop\Kuk\Kuk\Kuk\test.obj


Ja felet uppstår pga att du har deklarationen för den aktuella templateinstansens metod i två obj moduler, main.obj och test.obj, Länkarna kan inte välja att ignorera den ena av dem och måste därför pytsa ut ett felmeddelande. Bara en sådan deklaration får förekomma. Ibland kan man sätta in extern framför så att länkaren kan fortsätta att leta tills den hittas i källkodsträdet. Ända sedan templates började användas i C++ så har dessa felmeddelanden varit mycket kryptiska och borde snyggats till, felmeddelandena alltså. Jag tro det är lika dåligt i tex gcc - med kryptiska felmeddelanden alltså kring templates och länkningen.
Länkaren producerar vanligen kryptiska sådana här meddelanden Och bör snyggas upp

Citat:
Ursprungligen postat av kak
Har inte provat i Visual Studio, men du har måste instantiera Scandinavia<int> någon stans där metod-definitionerna är synliga.
Alternativ 1: Flytta definitionerna till header-filen.
Alternativ 2: Instantiera explicit i cpp-filen "template class Scandinavia<int>;"

Alternativ 2 har naturligtvis nackdelen att du måste känna till alla tänkbara typer T som någon kan vilja använda.

Om headerfilerna har olika användningsområden i olika sammanhang så kan man generera villkorlig kompiliering för att få de templates man behöver tex:

#ifdef USE_INT_TEMPLATES
template class Scandinavia<int>;"
#endif
#ifdef USE_DOUBLE_TEMPLATES
template class Scandinavia<double>;"
#endif
#ifdef USE_CHAR_TEMPLATES
template class Scandinavia<char>;"
#endif

Med att sätta defines i flaggor till kompilatorn så kommer den att välja vilka typer du kan behöva
Och ett praktiskt sätt att garantera att kompilatorn skapar den kod du behöver för en visst ändamål

Citat:
Ursprungligen postat av enowen
Gjorde rent din kod lite, precis som kek säger så behöver du en instantiera din template klass. Flytta implementation till din header fil istället.

Raden :
template class Scandinavia<int>; - är den viktiga

Snyggt gjort av enowen och han har alltid rätt Man borde kunna dela ut guldstjärnor här till
de som håller hög klass här, tack för det !

Citat:
Ursprungligen postat av karatekungen
Tack båda två! Det fungerar nu.

Jo vi brukar kunna fixa sådant.

Jag har själv skrivit några olika textprocessningsprogram som kan ändra sådant i källkoden direkt men det blir rätt rörigt att få det att fungera.

En del använder istället macron istf T, lite svårt att förklara men det går faktiskt
Tex
#define TYPE01 int
#define TYPE02 double
Citera

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback