Newbee template problem

Hello,
I am relatively new to c++, just for a couple of days. Although I do have a very strong java background some of the problems of the c++ language ha a tendency to drive me nuts...

Currently I do have a problem with a template. The following source code won't compile and I don't have an idea why.

The following header contains the relevant parts of a class which is a wrapper around std::map.

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
#include <string>
#include <map>
#include <fstream>
#include <iostream>

template<typename keyClass, typename valueClass>
class PropertiesWrapper{
	public:
		PropertiesWrapper();
		virtual ~PropertiesWrapper();
		void setProperty(keyClass key, valueClass value);
	private:
		std::map<keyClass, valueClass> *test;
};

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::PropertiesWrapper() {
		test = new std::map<keyClass, valueClass>();
}

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::~PropertiesWrapper() {
		delete test;
}

template<class keyClass, class valueClass> void PropertiesWrapper<keyClass, valueClass>::setProperty(keyClass key, valueClass value) {
	// Beginning of compile error
	std::pair<std::map<keyClass, valueClass>::iterator, bool> success = test->insert(std::pair<keyClass, valueClass>(key, value));
	// End of compile error
	if(success.second){

	}
}


The compile error I get is
1
2
3
4
../src/PropertiesWrapper.h: In member function »void PropertiesWrapper<keyClass, valueClass>::setProperty(keyClass, valueClass)«:
../src/PropertiesWrapper.h:26: Fehler: Typ/Wert des Arguments 1 passt nicht in Template-Parameterliste für »template<class _T1, class _T2> struct std::pair«
../src/PropertiesWrapper.h:26: Fehler:   einen Typ erwartet, »std::map::iterator« erhalten
../src/PropertiesWrapper.h:26: Fehler: invalid type in declaration before »=« token


When I replace the line
std::pair<std::map<keyClass, valueClass>::iterator, bool> success = test->insert(std::pair<keyClass, valueClass>(key, value));
with
std::pair<std::map<std::string, std::string>::iterator, bool> success = test->insert(std::pair<keyClass, valueClass>(key, value));
then everything compiles without trouble.

Does anybody know what I am doing wrong here?

Yt,

Gunnar
Can you post the code that tests the template as well?

With
 
std::pair<std::map<std::string, std::string>::iterator, bool> success = test->insert(std::pair<keyClass, valueClass>(key, value));

replacement, it does not compile on MS Visual C++ 2008 Pro., neither.
Last edited on
1
2
3
4
typedef std::map <keyClass, valueClass> KeyToValue;
typedef KeyToValue::iterator Iterator;
typedef std::pair <Iterator, bool> Pair;
Pair success = test->insert(std::pair<keyClass, valueClass>(key, value));


It's just more readable.

MS VS 2005 compilers it
Last edited on
Thanks for your replies.


Robertlzw (42) Sep 10, 2009 at 4:53pm
Can you post the code that tests the template as well?

With
std::pair<std::map<std::string, std::string>::iterator, bool> success = test->insert(std::pair<keyClass, valueClass>(key, value));
replacement, it does not compile on MS Visual C++ 2008 Pro., neither.


The code to implement this is just a standard #include of the header file. This file looks like:
1
2
3
4
5
6
7
8
#include <iostream>
#include "PropertiesWrapper.h"
using namespace std;

int main() {
	cout << "Hello World!!!" << endl; // prints Hello World!!!
	return 0;
}


WRT Denis suggestions I've modified the code to be more readable. It now reads:
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
#include <string>
#include <map>
#include <fstream>
#include <iostream>

template<class keyClass, class valueClass>
class PropertiesWrapper {

	public:
		PropertiesWrapper();
		virtual ~PropertiesWrapper();
		void setProperty(keyClass key, valueClass value);
	private:
		std::map<keyClass, valueClass> *test;
};

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::PropertiesWrapper() {
	test = new std::map<keyClass, valueClass>();
}

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::~PropertiesWrapper() {
	delete test;
}

template<class keyClass, class valueClass> void PropertiesWrapper<keyClass, valueClass>::setProperty(keyClass key, valueClass value) {
	typedef std::map<keyClass, valueClass> KeyValueMap;
	// Beginning of compile error
	typedef std::map<keyClass, valueClass>::iterator KeyValueIterator;
	// End of compile error
	typedef std::pair<keyClass, valueClass> KeyValuePair;

	// Beginning of compile error
	std::pair<KeyValueIterator, bool> success = test->insert(KeyValuePair(key, value));
	// End of compile error
	std::cout << &(success) << std::endl;
}


The compiler output changed slightly, but still: It won't compile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

**** Build of configuration Debug for project SourceTest ****

make all 
Building file: ../src/SourceTest.cpp
Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/SourceTest.d" -MT"src/SourceTest.d" -o"src/SourceTest.o" "../src/SourceTest.cpp"
In file included from ../src/SourceTest.cpp:10:
../src/PropertiesWrapper.h: In member function »void PropertiesWrapper<keyClass, valueClass>::setProperty(keyClass, valueClass)«:
../src/PropertiesWrapper.h:28: Fehler: zu wenige Templateparameterlisten
../src/PropertiesWrapper.h:33: Fehler: »KeyValueIterator« wurde in diesem Gültigkeitsbereich nicht definiert
../src/PropertiesWrapper.h:33: Fehler: Templateargument 1 ist ungültig
../src/PropertiesWrapper.h:33: Fehler: invalid type in declaration before »=« token
make: *** [src/SourceTest.o] Fehler 1


The environment is linux with g++ 4.3.4
Yes, with the original version, the following code works if both keyClass, valueClass are ints.

This version works for ints.
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
#include <string>
#include <map>										
#include <fstream>
#include <iostream>

template<typename keyClass, typename valueClass>
class PropertiesWrapper{
	public:
		PropertiesWrapper();
		virtual ~PropertiesWrapper();
		void setProperty(int key, int value);
	private:
		std::map<int, int> *test;
};

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::PropertiesWrapper() {		
	test = new std::map<int, int>();
}

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::~PropertiesWrapper() {
		delete test;
}

template<class keyClass, class valueClass> void PropertiesWrapper<keyClass, valueClass>::setProperty(int key, int value) {
	typedef std::map<keyClass, valueClass> KeyToValue;
	typedef KeyToValue::iterator Iterator;
	typedef std::pair<Iterator, bool> Pair;
	Pair success = test->insert(std::pair<int, int>(key, value));
	if(success.second){
		std::cout << "Success !!!" << std::endl;
		std::pair<int, int> py = *success.first;
		std::cout << "key is " << py.first  << std::endl;
		std::cout << "value is " << py.second  << std::endl;
	}
}

int main() {

	int x = 5;
	int y = 6;
	PropertiesWrapper<int, int> pw;
	pw.setProperty(x,y);

	return 0;
}


However, if the parametrized types are keyClass, valueClass, it does not compile.
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
#include <string>
#include <map>										
#include <fstream>
#include <iostream>

template<typename keyClass, typename valueClass>
class PropertiesWrapper{
	public:
		PropertiesWrapper();
		virtual ~PropertiesWrapper();
		void setProperty(keyClass key, valueClass value);
	private:
		std::map<keyClass, valueClass> *test;
};

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::PropertiesWrapper() {
		test = new std::map<keyClass, valueClass>();
}

template<class keyClass, class valueClass> PropertiesWrapper<keyClass, valueClass>::~PropertiesWrapper() {
		delete test;
}

template<class keyClass, class valueClass> void PropertiesWrapper<keyClass, valueClass>
::setProperty(keyClass key, valueClass value) {
	typedef std::map<keyClass, valueClass> KeyToValue;
	typedef KeyToValue::iterator Iterator;
	typedef std::pair<Iterator, bool> Pair;
	Pair success = test->insert(std::pair<keyClass, valueClass>(key, value));
	
	if(success.second){

	}
}

class keyClass {};
class valueClass {};

int main() {

	keyClass key;
	valueClass value;
	PropertiesWrapper<keyClass, valueClass> pw;
	pw.setProperty(key,value);
	
	return 0;
}
Last edited on
You need

typedef typename KeyToValue::iterator Iterator;

(note typename)

also, why is test a pointer to a map and not just a map?

Hi jsmith,
that really helped a lot! Code now compiles. But I do not quite understand what this "typename" is doing there and why it is necessary. Could you please explain that?

WRT the pointer thing: You're probably right on this. I have quite a hard time switching from the java-everything-is-a-pointer view to the c++-world with its mixture of local objects, pointers to objects created with new and pointers to local objects. Gives me some headaches...

So: Thanks for "pointing" this out to me!

Yt,

Gunnar
See Item 42 of the Book "Effective C++" (3rd Edition) for the use of typename in this context. This might explain it:
1
2
3
4
template<typename C>
void print(const C& container)
{
C:const_iterator * x;


x can be a pointer to C: const_iterator if it is clear that C: const_iterator is a type; however, if C has a static data member named const_iterator, then the line says that the static data member C:const_iterator should be multiplied by x. To make things clear, you declare x is a pointer to type C: const_iterator via:

 
typename C:const_iterator * x; 
Last edited on
Ah ok, I see. In the first example the compiler (or whoever it is at this point) would assume that "C" is an object, not a type. So I have to declare "C" to be a type via typename.

Starts to begin making sense....

Thank you all for helping me solve this out! Great community!

Yt,

Gunnar
C is a type, but it might have a static data member named const_iterator. So one way to refer to the static data member is: C:const_iterator. Obviously, now C:const_iterator is a data, not a type in this context.
For my posted code to compile, the keyClass should define operator< for it to be used as key in the map:

1
2
3
4
5
6
7
8
9
class keyClass {
	std::string s;
public:
	keyClass(std::string x) : s(x) {}
	std::string getVal() const { return s;}
	bool operator< (const keyClass& rhs ) const {	// needed for compare key in the map
		return s < rhs.s;
	}
};


MS VC++ 2008 Pro works both ways:

 
typedef typename KeyToValue::iterator Iterator;   // preferred way 


 
typedef KeyToValue::iterator Iterator;   // MS VC++ 2008 Pro does not care about it, bad !!! 
Last edited on
Thanks again, didn't think about that C:const_iterator * x could be read as "multiply the static member of C named const_iterator with x"... You must admit that this is a bit weird for someone comming from the java-world...

So, back to the initial example:

1
2
3
4
5
6
7
8
template<class keyClass, class valueClass> void PropertiesWrapper<keyClass, valueClass>::setProperty(keyClass key, valueClass value) {
	typedef std::map<keyClass, valueClass> KeyValueMap;
	typedef class KeyValueMap::iterator KeyValueIterator;
	typedef std::pair<keyClass, valueClass> KeyValuePair;

	std::pair<KeyValueIterator, bool> success = test.insert(KeyValuePair(key, value));
	std::cout << &(success) << std::endl;
}


Line 2 says that the KeyValueMap is a std::map<blahblah> type.

Line 3 addresses the "iterator" type from the std::map. And since this "iterator" is itself a type and not a static member I do have to tell this to the compiler via the "typename" keyword. This "typename" therefore refers to the "iterator" type and not to the "KeyValueMap" type?

Btw, just read your last post about the operator "<". Thanks for this example. Was a bit unsure about how this could possibly work with arbitrary classes. The comparator needs to know how to compare both classes.

Yt,

Gunnar
Last edited on
We mean that Iterator is an object of type std::map<blabla>::iterator via
 
typename std::map<blabla>::iterator Iterator;


Thinking of it as:
 
typename C::iterator Iterator; // where C is std::map<blabla> 


Another possibility in C++ world:
1
2
3
4
5
class C {
static const int Iterator = 0;
public:
//blabla
};

We access Iterator (the static integer with a value of 10 in class C) via:
 
C:Iterator;   // 10 


The two C::Iterators above have totally different meanings. You got to tell the compiler which one you want via keyword typename.

NO, the operator>() is defined for keyClass::operator<(). It has nothing to do with class valueClass:

1
2
3
4
5
6
7
8
9
class keyClass {
	std::string s;
public:
	keyClass(std::string x) : s(x) {}
	std::string getVal() const { return s;}
	bool operator< (const keyClass& rhs ) const {	// needed for compare key in the map
		return s < rhs.s;
	}
};
Last edited on
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <string>
#include <map>										
#include <iostream>

template<typename keyClass, typename valueClass>
class PropertiesWrapper{
	public:
		PropertiesWrapper(std::map<keyClass, valueClass>&);
		void setProperty(keyClass& key, valueClass& value);
	private:
		std::map<keyClass, valueClass>& test;
};

template<class keyClass, class valueClass> 
PropertiesWrapper<keyClass, valueClass>::
PropertiesWrapper(std::map<keyClass, valueClass>& sm) : test(sm) { }

template<class keyClass, class valueClass> 
void PropertiesWrapper<keyClass, valueClass>
::setProperty(keyClass& key, valueClass& value) {
	typedef std::map<keyClass, valueClass> KeyToValue;
	typedef typename KeyToValue::iterator Iterator;
	typedef std::pair<Iterator, bool> Pair;
	Pair success = test.insert(std::pair<keyClass, valueClass>(key, value));
	
	if(success.second){
		std::cout << "Success !!!" << std::endl;
		std::pair<keyClass, valueClass> py = *success.first;
		std::cout << "key is " << py.first.getVal()  << std::endl;
		std::cout << "value is " << py.second.getVal() << std::endl;
	}
}

class keyClass {
	std::string s;
public:
	keyClass(std::string x) : s(x) {}
	std::string getVal() const { return s;}
	bool operator< (const keyClass& rhs ) const {	// needed for compare key in the map
		return s < rhs.s;
	}
};

class valueClass {
	int i;
public:
	valueClass(int x) : i(x) {}
	int getVal() const { return i; }
};

int main() {	
	keyClass key1("key1");
	valueClass value1(100);
	keyClass key2("key2");
	valueClass value2(200);	

	std::map<keyClass, valueClass> sm;
	PropertiesWrapper<keyClass, valueClass> pw(sm);

	pw.setProperty(key1,value1);
	pw.setProperty(key2,value2);

	return 0;
}
Last edited on
Robertlzw,
many thanks for your detailed replies. They really helped a lot and I appreciated it!
I now know what this "typename" does.

In my very first example at the top of the thread I had the lines
1
2
3
// Beginning of compile error
std::pair<std::map<keyClass, valueClass>::iterator, bool> success = test->insert(std::pair<keyClass, valueClass>(key, value));
// End of compile error 

which were back then completely without typdefs, hence a bit hard to read.

But to fix this line I would just have to modifiy it to:
 
std::pair<class std::map<keyClass, valueClass>::iterator, bool> success = test.insert(KeyValuePair(key, value));


with "class" being the magic keyword telling the compiler that std::map<keyClass,valueClass>::iterator refers to the class std::map with its type iterator.

Now the code (with some other minor fixes) compiles.

Wow, still a loooong way to go. And every day I have to convince myself to not go back to doing java. But every day this urge gets a bit weaker.

Once again, I am very thankfull to you Robertlzw and all the rest who helped me sort this out.

Yt,

Gunnar
Topic archived. No new replies allowed.