Forward declaration being ignored?

Hello,

I have the below class (which is basically a wrapper for std::vector), and one of the functions returns a variable of the type AInteger (which is basically a wrapper for int). Now the type AInteger is used multiple times throughout the class, yet the compiler starts complaining at a very specific position. When I remove the "getSize()" function, everything compiles just fine.

What am I doing wrong?

Here's the class:
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
#ifndef ALIST_H
#define ALIST_H

#include <vector>

#include "AInteger.h"

class AInteger;

template<typename VALUE>
class AList {
public:
	AList() {
	}

	AList(const std::vector<VALUE> list) {
		value = list;
	}

	~AList() {
	}

	operator const std::vector<VALUE>() const {
		return value;
	}

	std::vector<VALUE> toStdVector() const {
		return value;
	}

	VALUE operator [](const AInteger index) const {
		return value.at(index);
	}

	void add(const VALUE value) {
		this->value.push_back(value);
	}

	VALUE get(const AInteger index) const {
		return value[index];
	}

	AInteger getSize() const { // ERROR OCCURS HERE
		return value.size();
	}

	void remove(const AInteger index) {
		value.erase(index);
	}

private:
	std::vector<VALUE> value;
};

#endif 


Output:
1>------ Build started: Project: ALibrary, Configuration: Debug Win32 ------
1>  ASocket.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  AInteger.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  AHttpRequest.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  ABoolean.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  Generating Code...
1>  Compiling...
1>  AString.cpp
1>  Generating Code...
2>------ Build started: Project: BitHoarder, Configuration: Debug Win32 ------
2>  SystemHandler.cpp
2>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
2>  Main.cpp
2>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
2>  Generating Code...
========== Build: 0 succeeded, 2 failed, 0 up-to-date, 0 skipped ==========


If any more code is needed just ask and I will provide.

Thanks!
Last edited on
dont declare it and replace the return type by VALUE the compiler will instantiate the class itself. if you wish to instanciate explicitly the template class then move your implementation in a cpp file and add at the end of the cpp file :
1
2
3
#include "AInteger.h"

template class Alist<AInteger>;
Thanks for the help!

That's the thing though, only that function should return an AInteger. The template should be independent of that, so VALUE could be anything.

I've already figured out that the class will compile and run just fine if I replace AInteger getSize() const with AInteger & getSize() const although I'm not sure why, since that would simply create a variable which would go out of scope once the function is done.

Maybe it has something to do with template classes not being actual classes?
Last edited on
The point is that if you declare that the function returns an object of type AInteger then the compiler needs to know the complete definition of AInteger. Forward declaration won't cut it; you need the definition, so you need to actually include AInteger.h, or whatever header file contains that definition.

If you're just declaring the return value as a reference, then the compiler doesn't need to know the definition of AInteger, so forward declaration works. However...

[...] that would simply create a variable which would go out of scope once the function is done.

Well, no, not quite... you're creating a reference to a variable that has gone out of scope. But, yes, it's will lead to undefined behaviour, which is not what you want.
Last edited on
Ah okay, so that's why it would work.

But when you say definition of AInteger, you mean the functions containing the actual code right? Because those are in a .cpp file which I can't include. I've already included the header for AInteger ("AInteger.h") but that only contains the declarations.

How come this doesn't work in template classes, but if I do the exact same thing in non-template classes it works fine? Is it because template classes contain inline definitions?

Example:
Does not work:
"test1.h":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef TEST1_H
#define TEST1_H

#include "AInteger.h"

class AInteger;

template<typename TEST>
class Test1 {
public:
	Test1() {
	}

	~Test1() {
	}
	
	AInteger test() {
		return 5;
	}
};

#endif 


Works:
"test2.h"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef TEST2_H
#define TEST2_H

#include "AInteger.h"

class AInteger;

class Test2 {
public:
	Test2();
	~Test2();
	AInteger test();
};

#endif 

"test2.cpp"
1
2
3
4
5
6
7
8
9
Test2::Test2() {
}

Test2::~Test2() {
}
	
AInteger Test2::test() {
	return 5;
}
Last edited on
But when you say definition of AInteger, you mean the functions containing the actual code right? Because those are in a .cpp file which I can't include. I've already included the header for AInteger ("AInteger.h") but that only contains the declarations.


The class definition doesn't have to contain the definitions of the methods. When using a class (rather than just a reference to the class), the compiler needs to know things like the interface definition, and the size of the object. This means it needs to have the declarations of all the contents of the class, i.e. the stuff that needs to be in the class definition in the header file).

It doesn't need the definitions of the methods; those are resolved at link-time, and can be in another cpp file.

How come this doesn't work in template classes, but if I do the exact same thing in non-template classes it works fine? Is it because template classes contain inline definitions?

For templates, the compiler creates the entire class at compile-time, at the point where an object is created from the class template. This means that the entire definition of the class and its functions need to be available to the compiler.

It's one of the reasons I hate using templates, but modern design patterns rely heavily on them.

Oh, and the reason your test1.h doesn't work is because you're still using forward declaration for AInteger.
Last edited on
Okay thanks :)

So I need to have the definition of the AInteger class inside the template class. I thought #include "AInteger.h" was supposed to take care of that? Since AInteger.h contains the definition of AInteger. Is there a reason the compiler is ignoring #include "AInteger.h" completely?

I've tried removing the forward declaration "class AInteger;" but it only leads to more errors.

These are the contents of AInteger.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef AINTEGER_H
#define AINTEGER_H

class AInteger {
public:
	AInteger();
	AInteger(const int);
	~AInteger();

	operator const int() const;

	int toInt() const;

	AInteger & operator=(const AInteger &);
	AInteger & operator++();
	AInteger & operator--();

private:
	int value;
};

#endif 
Last edited on
If you had follow my recommandation, you would have this :

AList.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef ALIST_H
#define ALIST_H

#include <vector>

template<typename VALUE>
class AList 
{
	std::vector<VALUE> value;
public:
	AList();
	AList(const std::vector<VALUE> list);
	~AList();
	operator const std::vector<VALUE>() const;
	std::vector<VALUE> toStdVector() const;
	VALUE operator [](const VALUE index) const;
	void add(const VALUE value);
	VALUE get(const VALUE index) const;
	VALUE getSize() const;
	void remove(const VALUE index);
};

#endif  


AList.cpp
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
#include "AList.h"

template<typename VALUE>
AList<VALUE>::AList() 
{
}
template<typename VALUE>
AList<VALUE>::AList(const std::vector<VALUE> list)
{
	value = list;
}
template<typename VALUE>
AList<VALUE>::~AList()
{
}
template<typename VALUE>
AList<VALUE>::operator const std::vector<VALUE>() const
{
	return value;
}
template<typename VALUE>
std::vector<VALUE> AList<VALUE>::toStdVector() const
{
	return value;
}
template<typename VALUE>
VALUE AList<VALUE>::operator [](const VALUE index) const
{
	return value.at(index);
}
template<typename VALUE>
void AList<VALUE>::add(const VALUE value)
{
	this->value.push_back(value);
}
template<typename VALUE>
VALUE AList<VALUE>::get(const VALUE index) const
{
	return value[index];
}
template<typename VALUE>
VALUE AList<VALUE>::getSize() const
{ 
	return value.size();
}
template<typename VALUE>
void AList<VALUE>::remove(const VALUE index)
{
	typename std::vector<VALUE>::iterator it = value.begin();
	it += index;
	value.erase(it);
}

#include "AInteger.h"

template class AList<AInteger>;



AInteger.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef AINTEGER_H
#define AINTEGER_H

class AInteger 
{
	int value;
public:
	AInteger();
	AInteger(const int);
	~AInteger();
	operator const int() const;
	int toInt() const;
	AInteger & operator=(const AInteger &);
	AInteger & operator++();
	AInteger & operator--();
};

#endif  


AInteger.cpp

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
#include "AInteger.h"
AInteger::AInteger()
: value(0)
{
}
AInteger::AInteger(const int _value)
: value(_value)
{
}
AInteger::~AInteger()
{
}
AInteger::operator const int() const
{
	return value;
}
int AInteger::toInt() const
{
	return value;
}
AInteger & AInteger::operator=(const AInteger & other)
{
	value = other.value;
	return *this;
}
AInteger & AInteger::operator++()
{
	++value;
	return *this;
}
AInteger & AInteger::operator--()
{
	--value;
	return *this;
}


main.cpp (for testing ...)

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

#include "AList.h"

int main(int argc, char* argv[])
{
	AInteger value;

	AList<AInteger> list;

	return 0;
}
I appreciate the effort, I really do, but that's not what I meant. By doing AList<AInteger> you're defining VALUE to be of the type AInteger. That's not my goal. AInteger is not the VALUE of the AList, it is just the return type of one of its functions. So for example, if I declare an AList like this: AList<std::string> list; the function getSize() should still return something of the type AInteger. This is because getSize() should always return the amount of items in the list. If in your code the list were to be declared as AList<std::string>, getSize() would return something which would be a type of std::string, which doesn't make any sense. So no matter what VALUE is, getSize() should return something of the type AInteger (which is a wrapper class I wrote for the default type int).
That is, of course, unless I'm missing something.
> So I need to have the definition of the AInteger class inside the template class.

We can (probably should) make AInteger a dependant name so that its look up is postponed to the point of actual instantiation (phase two of the two-phase lookup). We can then postpone the definition of AInteger till we actually use it. http://www.cplusplus.com/forum/beginner/103508/#msg557627

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <vector>

// #include "AInteger.h" // *** remove

class AInteger; // declare AInteger

template< typename VALUE_TYPE, typename SIZE_TYPE = AInteger > // use AInteger as a default
class AList {
public:
    AList() {
	}

	AList(const std::vector<VALUE_TYPE> list) {
		value = list;
	}

	~AList() {
	}

	operator const std::vector<VALUE_TYPE>() const {
		return value;
	}

	std::vector<VALUE_TYPE> toStdVector() const {
		return value;
	}

	VALUE_TYPE operator []( SIZE_TYPE index) const {
		return value.at(index);
	}

	void add(const VALUE_TYPE value) {
		this->value.push_back(value);
	}

	VALUE_TYPE get( SIZE_TYPE index) const {
		return value[index];
	}

	SIZE_TYPE getSize() const { // ERROR OCCURS HERE
		return value.size();
	}

	void remove( SIZE_TYPE index) {
		value.erase( value.begin() + index);
	}

private:
	std::vector<VALUE_TYPE> value;
};

/////////////////////////////////////////////////////////////////////////////////////

#include <iostream>

class AInteger { // *** define AInteger before we use it (ie. #include "AInteger.h")
public:
	// AInteger();
	AInteger( /*const*/ int v = 0 ) : value(v) {} // removed const qualifier on prvalue
	// ~AInteger(); // *** implicitly declared

	operator /*const*/ int() const { return value ; } // removed const qualifier on prvalue

	int toInt() const;

	// AInteger & operator=(const AInteger &); // *** implicitly declared
	AInteger & operator++() { ++value ; return *this ; }
	AInteger & operator--();

private:
	int value;
};

int main()
{
    AList<double> my_list ;

    for( double v : { 1.23, 4.56, 7.89 } ) my_list.add(v) ;
    for( AInteger i = 0 ; i < my_list.getSize() ; ++i ) std::cout << my_list.get(i) << ' ' ;
    std::cout << '\n' ;

    AInteger pos = 1 ;
    my_list.remove(pos) ;
    for( AInteger i = 0 ; i < my_list.getSize() ; ++i ) std::cout << my_list.get(i) << ' ' ;
    std::cout << '\n' ;
}

http://coliru.stacked-crooked.com/a/8d310de965c147f7
http://rextester.com/ZDMOQS16222
Last edited on
then make the method a fouble template, you wont need to double template all the class.
Why double template? The OP wants that method to always return the same type, regardless of what type the class is templated on. Why on earth would you make that return type templated?
I total reject what I said yesterday , then

for the AList.h
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

#ifndef ALIST_H
#define ALIST_H

#include <vector>

//fwd declaration
class AInteger;

template<typename VALUE>
class AList 
{
	std::vector<VALUE> value;
public:
	AList();
	AList(const std::vector<VALUE> list);
	~AList();
	operator const std::vector<VALUE>() const;
	std::vector<VALUE> toStdVector() const;
	VALUE operator [](const VALUE index) const;
	void add(const VALUE value);
	VALUE get(const VALUE index) const;
	AInteger getSize() const;
	void remove(const VALUE index);
};

#endif  


for the AList.cpp

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
#include "AList.h"

#include "AInteger.h"

template<typename VALUE>
AList<VALUE>::AList() 
{
}
template<typename VALUE>
AList<VALUE>::AList(const std::vector<VALUE> list)
{
	value = list;
}
template<typename VALUE>
AList<VALUE>::~AList()
{
}
template<typename VALUE>
AList<VALUE>::operator const std::vector<VALUE>() const
{
	return value;
}
template<typename VALUE>
std::vector<VALUE> AList<VALUE>::toStdVector() const
{
	return value;
}
template<typename VALUE>
VALUE AList<VALUE>::operator [](const VALUE index) const
{
	return value.at(index);
}
template<typename VALUE>
void AList<VALUE>::add(const VALUE value)
{
	this->value.push_back(value);
}
template<typename VALUE>
VALUE AList<VALUE>::get(const VALUE index) const
{
	return value[index];
}
template<typename VALUE>
AInteger AList<VALUE>::getSize() const
{ 
	return value.size();
}
template<typename VALUE>
void AList<VALUE>::remove(const VALUE index)
{
	typename std::vector<VALUE>::iterator it = value.begin();
	it += index;
	value.erase(it);
}

template class AList<AInteger>;
Thanks Ericool, the errors saying that AInteger is undefined have now disappeared. But, in their place came a whole lot of new linker errors, all "unresolved external symbol". They're all very cryptic and don't give me any clue at all on how to solve them. They all point to line 1 of the AList.h file.

If it is needed I could possibly upload the project somewhere so someone could take a look. This problem has been bugging me for a week now :(

EDIT: I'm also receiving this error:
AList.obj : warning LNK4221: This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library
Last edited on
did you rebuild ?

did you put the implementation of AInteger in a cpp ?

because it works fine on my side, maybe you can show us the error comments
> a whole lot of new linker errors, all "unresolved external symbol"

Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl
Topic archived. No new replies allowed.