Templates confusion

I've been trying to get this piece of code to work with a template but it won't. I don't know how to get the template to work with a class constructor... Don't understand why this won't work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//MY HEADER FILE
#pragma once
#ifndef SAVE_H
#define SAVE_H
#include <fstream>
#include <string>
template <typename special> class save {
private:
	int size;
	std::fstream A;
public:
	save(std::string txt);
	~save();
	void read(std::string txt) {
		A.open(txt, std::fstream::in);
		if (A.fail()) {
			A.open(txt, std::fstream::out);

		}
		A.close();
	}
	void write(std::string txt, special Z[]) {
		size = sizeof(Z) / sizeof(Z[0]);
 		A.open(txt, std::fstream::out | std::fstream::trunc);
		for (unsigned int i = 0; i < size; i++) {
			A << Z[i];
		}
	}
};
#endif 

1
2
3
4
5
6
7
8
9
10
11
12
#include "save.h"
//MY CPP
save::save(std::string txt)
{
	A.open(txt, std::fstream::out);
	A.close();
}


save::~save()
{
}
You need to add the template stuff when defining the functions.

1
2
3
4
5
6
template <typename special>
save<special>::save(std::string txt)
{
	A.open(txt, std::fstream::out);
	A.close();
}


You also need to put this in the header file because the full definition needs to be available when you use the template.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma once
#ifndef SAVE_H
#define SAVE_H
#include <fstream>
#include <string>
template <typename special> class save {
private:
	int size;
	std::fstream A;
public:
	save<special>(std::string txt);
	~save<special>();
	void read(std::string txt) {
		A.open(txt, std::fstream::in);
		if (A.fail()) {
			A.open(txt, std::fstream::out);

		}
		A.close();
	}
	void write(std::string txt, special Z[]) {
		size = sizeof(Z) / sizeof(Z[0]);
 		A.open(txt, std::fstream::out | std::fstream::trunc);
		for (unsigned int i = 0; i < size; i++) {
			A << Z[i] << flush; 
		}
	}
};
#endif 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "save.h"

template<typename special>
save<special>::save(std::string txt)
{
	A.open(txt, std::fstream::out);
	A.close();
}

template<typename special>
save<special>::~save()
{

}

Well now it doesn't give errors directly, however I'm now unable to call within a different file. It says that 'save' class has no constructors, and that use of class template requires template argument list also indirect from std::string....
You should not have changed anything in the class definition.
Yeah but now I'm getting link errors?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#pragma once
#ifndef SAVE_H
#define SAVE_H
#include <fstream>
#include <string>
template <typename special> class save {
private:
	int size;
	std::fstream A;
public:
	save(std::string txt);
	~save();
	void read(std::string txt) {
		A.open(txt, std::fstream::in); 
		if (A.fail()) {
			A.open(txt, std::fstream::out);

		}
		A.close();
	}
	void write(std::string txt, special Z[]) {
		size = sizeof(Z) / sizeof(Z[0]);
 		A.open(txt, std::fstream::out | std::fstream::trunc);
		for (unsigned int i = 0; i < size; i++) {
			A << Z[i] << flush; 
		}
	}
};
#endif 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "save.h"

template<typename special>
save<special>::save(std::string txt)
{
}

template<typename special>
save<special>::~save()
{

}


In my main I try this
1
2
3
4
5
6
7
8
9
10
11
12
#include "save.h"
using std::cin;
using std::cout;
using std::string;
using std::endl;
int main() {
	char yes;
	string a = "yes";
	save<string> A(a);
	cin >> yes;
        return 0;
}

Yet now I'm getting link errors.
Last edited on
See 'Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?' here:
https://isocpp.org/wiki/faq/templates
Thanks @JLBorges, really helpful information on there in general!

I did this and well it works, not sure if it was the best way, buuuuuut it gets the job done, what do you think, anyways thanks!!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#pragma once
#ifndef SAVE_H
#define SAVE_H
#include <fstream>
#include <string>
template <typename special> class save {
private:
	int size;
	std::fstream A;
public:
	save(std::string txt);
	save();
	~save();
	void read(std::string txt) {
		A.open(txt, std::fstream::in); 
		if (A.fail()) {
			A.open(txt, std::fstream::out);

		}
		A.close();
	}
	void write(std::string txt, special Z[]) {
		size = sizeof(Z) / sizeof(Z[0]);
 		A.open(txt, std::fstream::out | std::fstream::trunc);
		for (unsigned int i = 0; i < size; i++) {
			A << Z[i] << std::endl; 
		} 
	}
};
template<typename special>
inline save<special>::save(std::string txt)
{
	A.open(txt, std::fstream::out);
	A << "First number = 1";
	A.close();
}

template<typename special>
inline save<special>::~save()
{

}

template<typename special>
inline save<special>::save()
{
}
#endif 
Note that you have choice where to define things. The constructors and the destructor could be defined inside the class definition like you did with the read and write functions, or you could define the read and write functions outside the class definition like you did with the constructors and the destructor.
It is not true what you guys say.

The OP could have retained his original solution by just explicitly declaring a template in a header file, and leave constructor/destructor definition in source files:


save.h header file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//MY HEADER FILE
#ifndef SAVE_H
#define SAVE_H

#include <fstream>
#include <string>

template <typename special>
class save {
private:
	int size;
	std::fstream A;
public:
	save(std::string txt);
	~save();
	void read(std::string txt) {
		A.open(txt, std::fstream::in);
		if (A.fail()) {
			A.open(txt, std::fstream::out);

		}
		A.close();
	}
	void write(std::string txt, special Z[]) {
		size = sizeof(Z) / sizeof(Z[0]);
		A.open(txt, std::fstream::out | std::fstream::trunc);
		for (unsigned int i = 0; i < size; i++) {
			A << Z[i];
		}
	}
};

// explicit template declaration, (extern instantiation for string and int)
extern template class save<int>;
extern template class save<std::string>;

#endif   


save.cc source file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "save.h"

template<typename special>
save<special>::save(std::string txt)
{
	A.open(txt, std::fstream::out);
	A.close();
}

template<typename special>
save<special>::~save()
{

}

// explicit template instantiation for int and string
// note that we do this once class definition is well known.
template class save<int>;
template class save<std::string>;


main.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "save.h"
#include <iostream>

using std::cin;
using std::cout;
using std::string;
using std::endl;
int main() {
	char yes;
	string a = "yes";
	save<string> A(a);
	cin >> yes;
	return 0;
}

Last edited on
@codekiddy,
If I understand correctly that will work only for the types explicitly declared, which is fine if you want to limit the types the template class can manage but IMO loses a lot of generalization. Or, of course, explicitly instantiate for every type.

An easy way to separate definition from implementation for templates is to simply #include the implementation at the bottom of the template declaration. Whether this is the best method is subjective.
@naraku
Yes, unfortunately it will work only for types explicitly instantiated.
This approach is most useful in your own projects, in which case you know what you need.

Also the purpose of extern keyword is to reduce compile time, not to hide implementation.
Topic archived. No new replies allowed.