templates: parent class member variables not visible in inherited class - c++

I have the following 4 files:
arrayListType.h: Declare and define arrayListType class as a template
unorderedArrayListType.h: Inherited from arrayListType class and Declares and defines unorderedArrayListType as a template.
main1.cpp: Test program to test unorderedArrayListType class.
Makefile
I get a compile error saying when accessing the protected variables of arrayListType in unorderedArrayListType for example: "length not declared in this scope", "list not declared in this scope", where length and list are protected variables in arrayListType class.
The following are the codes:
arrayListType.h
#ifndef H_arrayListType
#define H_arrayListType
#include <iostream>
using namespace std;
template <class elemType>
class arrayListType
{
public:
const arrayListType<elemType>&operator=(const arrayListType<elemType>&);
bool isEmpty() const;
bool isFull() const;
int listSize() const;
int maxListSize() const;
void print() const;
bool isItemAtEqual(int location, const elemType& item) const;
virtual void insertAt(int location, const elemType& insertItem) = 0;
virtual void insertEnd(const elemType& insertItem) = 0;
void removeAt(int location);
void retrieveAt(int location, elemType& retItem) const;
virtual void replaceAt(int location, const elemType& repItem) = 0;
void clearList();
virtual int seqSearch(const elemType& searchItem) const;
virtual void remove(const elemType& removeItem) = 0;
arrayListType(int size = 100);
arrayListType(const arrayListType<elemType>& otherList);
virtual ~arrayListType();
protected:
elemType *list;
int length;
int maxSize;
};
template <class elemType>
bool arrayListType<elemType>::isEmpty() const
{
return (length == 0);
}
// remaining non-virtual functions of arrayListType class
#endif
unorderedArrayListType.h
#ifndef H_unorderedArrayListType
#define H_unorderedArrayListType
//#include <iostream>
#include "arrayListType.h"
//using namespace std;
template <class elemType>
class unorderedArrayListType: public arrayListType<elemType>
{
public:
void insertAt(int location, const elemType& insertItem);
void insertEnd(const elemType& insertItem);
void replaceAt(int location, const elemType& repItem);
int seqSearch(const elemType& searchItem) const;
void remove(const elemType& removeItem);
unorderedArrayListType(int size = 100);
};
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = length; i > location; i--)
list[i] = list[i - 1];
list[location] = insertItem;
length++;
}
// Remaining virtual functions that need to be defined by the inherited class
#endif
main1.cpp
#include <iostream>
#include "unorderedArrayListType.h"
using namespace std;
int main()
{
unorderedArrayListType<int> intList(25);
int number;
cout<<"Line 3: Enter 8 integers: ";
for(int count = 0; count < 8; count++)
{
cin>>number;
intList.insertEnd(number);
}
cout<<"Line 8: intList: ";
intList.print();
cout<<endl;
}
Makefile:
all: main1
main1.o: main1.cpp
g++ -c -Wall main1.cpp
main1: main1.o
g++ -Wall main1.o -o main
clean:
rm -f *.o *~ main1
The following is the compilation error:
make
g++ -c -Wall main1.cpp
In file included from main1.cpp:2:
unorderedArrayListType.h: In member function 'void unorderedArrayListType<elemType>::insertAt(int, const elemType&)':
unorderedArrayListType.h:30: error: 'length' was not declared in this scope
unorderedArrayListType.h:31: error: 'list' was not declared in this scope
unorderedArrayListType.h:33: error: 'list' was not declared in this scope
More functions of unorderedArrayListType listed and protected variables indicated as not declared in the scope. Wondering what could be the error.
New error:
make
g++ -Wall main1.o -o main
Undefined first referenced
symbol in file
arrayListType<int>::seqSearch(int const&) constmain1.o
ld: fatal: Symbol referencing errors. No output written to main
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `main1'

This is because the template parent of a template class is not instantiated during the compilation pass that first examines the template. These names appear to be non-dependent on the particular template instantiation, and therefore the definitions need to be available. (If you never look at the definition of arrayListType, then reading the code of unorderedArrayListType it would appear the list and length need to be some sort of globals.)
You'll need to tell the compiler explicitly that the names are in fact dependent on the instantiation of the parent.
One way, using this-> before all the inherited names: this->list, this->length.
Another way, using declarations: using arrayListType<elemType>::length; etc (for example in the private section of the derived class).
A FAQ entry on this: https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members

An extended comment on UncleBens' answer.
It is always good to keep in mind that class templates are not classes. They are templates. One way to look at it: In C++, classes are not objects. You need to instantiate a class to create an object. A similar concept applies to class templates and classes. Just as class instantiation creates an object, class template instantiation creates a class.
Until the template is instantiated, that inheritance relationship you set up between unorderedArrayListType and arrayListType doesn't quite exist. The compiler does not know if you are going to define a partial template instantiation of arrayListType that doesn't have length and list as data members. You need to give the compiler a hand in your unorderedArrayListType by using this->length and this->list or some other construct that tells the compiler that you do expect these to be data members.
Suppose you use this->length in unorderedArrayListType, and suppose that someone comes along and writes a partial template instantiation of arrayListType<FooType> that does not have length and list as data members. Now instantiating an unorderedArrayListType<FooType> will result in compile time error. But since you aren't going to do that (you aren't going to do that, are you?), using this->length will be OK.

I would try two things:
1. Use this-> (which is generally a good idea to do with templates).
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = this->length; i > location; i--)
this->list[i] = this->list[i - 1];
this->list[location] = insertItem;
this->length++;
}
2. Typedef the parent and use it when accessing the parent members:
template <class elemType>
class unorderedArrayListType: public arrayListType<elemType>
{
typedef arrayListType<elemType> Parent;
...
}
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = Parent::length; i > location; i--)
Parent::list[i] = Parent::list[i - 1];
Parent::list[location] = insertItem;
Parent::length++;
}

Related

Fails to identify members inherited from another nested class that inherits from a nested class [duplicate]

I have the following 4 files:
arrayListType.h: Declare and define arrayListType class as a template
unorderedArrayListType.h: Inherited from arrayListType class and Declares and defines unorderedArrayListType as a template.
main1.cpp: Test program to test unorderedArrayListType class.
Makefile
I get a compile error saying when accessing the protected variables of arrayListType in unorderedArrayListType for example: "length not declared in this scope", "list not declared in this scope", where length and list are protected variables in arrayListType class.
The following are the codes:
arrayListType.h
#ifndef H_arrayListType
#define H_arrayListType
#include <iostream>
using namespace std;
template <class elemType>
class arrayListType
{
public:
const arrayListType<elemType>&operator=(const arrayListType<elemType>&);
bool isEmpty() const;
bool isFull() const;
int listSize() const;
int maxListSize() const;
void print() const;
bool isItemAtEqual(int location, const elemType& item) const;
virtual void insertAt(int location, const elemType& insertItem) = 0;
virtual void insertEnd(const elemType& insertItem) = 0;
void removeAt(int location);
void retrieveAt(int location, elemType& retItem) const;
virtual void replaceAt(int location, const elemType& repItem) = 0;
void clearList();
virtual int seqSearch(const elemType& searchItem) const;
virtual void remove(const elemType& removeItem) = 0;
arrayListType(int size = 100);
arrayListType(const arrayListType<elemType>& otherList);
virtual ~arrayListType();
protected:
elemType *list;
int length;
int maxSize;
};
template <class elemType>
bool arrayListType<elemType>::isEmpty() const
{
return (length == 0);
}
// remaining non-virtual functions of arrayListType class
#endif
unorderedArrayListType.h
#ifndef H_unorderedArrayListType
#define H_unorderedArrayListType
//#include <iostream>
#include "arrayListType.h"
//using namespace std;
template <class elemType>
class unorderedArrayListType: public arrayListType<elemType>
{
public:
void insertAt(int location, const elemType& insertItem);
void insertEnd(const elemType& insertItem);
void replaceAt(int location, const elemType& repItem);
int seqSearch(const elemType& searchItem) const;
void remove(const elemType& removeItem);
unorderedArrayListType(int size = 100);
};
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = length; i > location; i--)
list[i] = list[i - 1];
list[location] = insertItem;
length++;
}
// Remaining virtual functions that need to be defined by the inherited class
#endif
main1.cpp
#include <iostream>
#include "unorderedArrayListType.h"
using namespace std;
int main()
{
unorderedArrayListType<int> intList(25);
int number;
cout<<"Line 3: Enter 8 integers: ";
for(int count = 0; count < 8; count++)
{
cin>>number;
intList.insertEnd(number);
}
cout<<"Line 8: intList: ";
intList.print();
cout<<endl;
}
Makefile:
all: main1
main1.o: main1.cpp
g++ -c -Wall main1.cpp
main1: main1.o
g++ -Wall main1.o -o main
clean:
rm -f *.o *~ main1
The following is the compilation error:
make
g++ -c -Wall main1.cpp
In file included from main1.cpp:2:
unorderedArrayListType.h: In member function 'void unorderedArrayListType<elemType>::insertAt(int, const elemType&)':
unorderedArrayListType.h:30: error: 'length' was not declared in this scope
unorderedArrayListType.h:31: error: 'list' was not declared in this scope
unorderedArrayListType.h:33: error: 'list' was not declared in this scope
More functions of unorderedArrayListType listed and protected variables indicated as not declared in the scope. Wondering what could be the error.
New error:
make
g++ -Wall main1.o -o main
Undefined first referenced
symbol in file
arrayListType<int>::seqSearch(int const&) constmain1.o
ld: fatal: Symbol referencing errors. No output written to main
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `main1'
This is because the template parent of a template class is not instantiated during the compilation pass that first examines the template. These names appear to be non-dependent on the particular template instantiation, and therefore the definitions need to be available. (If you never look at the definition of arrayListType, then reading the code of unorderedArrayListType it would appear the list and length need to be some sort of globals.)
You'll need to tell the compiler explicitly that the names are in fact dependent on the instantiation of the parent.
One way, using this-> before all the inherited names: this->list, this->length.
Another way, using declarations: using arrayListType<elemType>::length; etc (for example in the private section of the derived class).
A FAQ entry on this: https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members
An extended comment on UncleBens' answer.
It is always good to keep in mind that class templates are not classes. They are templates. One way to look at it: In C++, classes are not objects. You need to instantiate a class to create an object. A similar concept applies to class templates and classes. Just as class instantiation creates an object, class template instantiation creates a class.
Until the template is instantiated, that inheritance relationship you set up between unorderedArrayListType and arrayListType doesn't quite exist. The compiler does not know if you are going to define a partial template instantiation of arrayListType that doesn't have length and list as data members. You need to give the compiler a hand in your unorderedArrayListType by using this->length and this->list or some other construct that tells the compiler that you do expect these to be data members.
Suppose you use this->length in unorderedArrayListType, and suppose that someone comes along and writes a partial template instantiation of arrayListType<FooType> that does not have length and list as data members. Now instantiating an unorderedArrayListType<FooType> will result in compile time error. But since you aren't going to do that (you aren't going to do that, are you?), using this->length will be OK.
I would try two things:
1. Use this-> (which is generally a good idea to do with templates).
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = this->length; i > location; i--)
this->list[i] = this->list[i - 1];
this->list[location] = insertItem;
this->length++;
}
2. Typedef the parent and use it when accessing the parent members:
template <class elemType>
class unorderedArrayListType: public arrayListType<elemType>
{
typedef arrayListType<elemType> Parent;
...
}
template <class elemType>
void unorderedArrayListType<elemType>::insertAt(int location, const elemType& insertItem)
{
for(int i = Parent::length; i > location; i--)
Parent::list[i] = Parent::list[i - 1];
Parent::list[location] = insertItem;
Parent::length++;
}

Template Class "Sequence/Class Name does not name a type" Error

I'm currently trying to learn templates in C++ and am unable to make sense of this current Error.
I was trying to write a sequence class based out of a code example from the book. I keep getting this error repeatedly.
I am required to write this template modularly. The functions work, but i can't find the source of the errors. I have tried changing name of .cpp file with no success.
This code is nearly identical to that which i found and ran in the book which compiled with no issues.
Here is my Code for header and implementation files.
#ifndef SEQUENCE_H
#define SEQUENCE_H
#include <cstdlib> // provides size_t
namespace CS3358_FA17_A04_sequenceOfNum
{
template <class Item>
class sequence
{
public:
// TYPEDEFS and MEMBER CONSTANTS
typedef Item value_type;
typedef size_t size_type;
static const size_type CAPACITY = 10;
// CONSTRUCTOR
sequence();
// MODIFICATION MEMBER FUNCTIONS
void start();
void end();
void advance();
void move_back();
void add(const Item& entry);
void remove_current();
// CONSTANT MEMBER FUNCTIONS
size_type size() const;
bool is_item() const;
Item current() const;
private:
Item data[CAPACITY];
size_type used;
size_type current_index;
};
}
#include "sequence.hpp"
.HPP File:
namespace CS3358_FA17_A04_sequenceOfNum
{
template <class Item>
sequence<Item>::sequence() : used(0), current_index(0) { }
template <class Item>
void sequence<Item>::start() { current_index = 0; }
template <class Item>
void sequence<Item>::end()
{
current_index = (used > 0) ? used - 1 : 0;
}
template <class Item>
void sequence<Item>::advance()
{
assert( is_item() );
++current_index;
}
template <class Item>
void sequence<Item>::move_back()
{
assert( is_item() );
if (current_index == 0)
current_index = used;
else
--current_index;
}
template <class Item>
void sequence<Item>::add(const Item& entry)
{
assert( size() < CAPACITY );
size_type i;
if ( ! is_item() )
{
if (used > 0)
for (i = used; i >= 1; --i)
data[i] = data[i - 1];
data[0] = entry;
current_index = 0;
}
else
{
++current_index;
for (i = used; i > current_index; --i)
data[i] = data[i - 1];
data[current_index] = entry;
}
++used;
}
template <class Item>
void sequence<Item>::remove_current()
{
assert( is_item() );
size_type i;
for (i = current_index + 1; i < used; ++i)
data[i - 1] = data[i];
--used;
}
template <class Item>
typename sequence<Item>::size_type sequence<Item>::size() const { return used;}
template <class Item>
bool sequence<Item>::is_item() const { return (current_index < used); }
template <class Item>
typename sequence<Item>::Item sequence<Item>::current() const
{
assert( is_item() );
return data[current_index];
}
}
Error's Are:
sequence does not name a type -> this occurs in every function
expected initializer before '<' every place a template specifier is used.
I have written many templates before, but i have always defined and implemented in the same file because of compiler and linker issues.
Any help would be much appreciated, here is a complete list of errors and their lines.
g++ -Wall -ansi -pedantic -c sequence.cpp
sequence.cpp:48:4: error: ‘sequence’ does not name a type
sequence<Item>::sequence() : used(0), current_index(0) { }
^
sequence.cpp:51:17: error: expected initializer before ‘<’ token
void sequence<Item>::start() { current_index = 0; }
^
sequence.cpp:54:17: error: expected initializer before ‘<’ token
void sequence<Item>::end()
^
sequence.cpp:60:17: error: expected initializer before ‘<’ token
void sequence<Item>::advance()
^
sequence.cpp:67:17: error: expected initializer before ‘<’ token
void sequence<Item>::move_back()
^
sequence.cpp:77:17: error: expected initializer before ‘<’ token
void sequence<Item>::add(const Item& entry)
^
sequence.cpp:102:17: error: expected initializer before ‘<’ token
void sequence<Item>::remove_current()
^
sequence.cpp:114:4: error: ‘sequence’ does not name a type
sequence<Item>::size_type sequence<Item>::size() const { return used; }
^
sequence.cpp:117:17: error: expected initializer before ‘<’ token
bool sequence<Item>::is_item() const { return (current_index < used); }
^
sequence.cpp:120:4: error: ‘sequence’ does not name a type
sequence<Item>::Item sequence<Item>::current() const
Here is Makefile, Also edited my changes in the file extensions.
a4s1: sequence.o sequenceTest.o
g++ sequence.o sequenceTest.o -o a4s1
sequence.o: sequence.hpp sequence.h
g++ -Wall -ansi -pedantic -c sequence.hpp
sequenceTest.o: sequenceTest.cpp sequence.hpp sequence.h
g++ -Wall -ansi -pedantic -c sequenceTest.cpp
test:
./a4s1 auto < a4test.in > a4test.out
clean:
#rm -rf sequence.o sequenceTest.o
cleanall:
#rm -rf sequence.o sequenceTest.o a4s1
"include the implementation in the header file" doesn't mean #include <sequence.cpp>, it means to actually have everything in sequence.h.
The possible solutions are:
Move everything from sequence.cpp to sequence.h and delete sequence.cpp.
Rename sequence.cpp to sequence.imp or some other extension and never compile it by itself (remove it from the makefile altogether). It will be compiled in the .cpp file(s) that #include sequence.h (this is probably what is asked of you).
Keep sequence.cpp but #include <sequence.h> at the top and use explicit template instantiation.
Templates have to be defined in every compilation unit they're used in, see here. This usually means, that their implementation has to be put inside a header file. If you want to put the implementation in a separat file anyway, you can create an "implementation file" and include it at the end of your header. That seems to be the strategy your professor is recommending.
However, the implementation file may not be treated as separate compilation unit by your built system. It should end in .imp or .hpp or something else but not .cpp.

Template file not working correctly in CodeLite?

Every time I create a new project in my workplace I run into the problem with templates. For example, I'll create a new class, which CodeLite will create a .h file and a .cpp file for me, and then I'll change that .cpp file into a .template by renaming the file. It sometimes works, and sometimes doesn't. Sometimes I have to clean my workplace for it to work, other times I need to exit out of CodeLite and reopen it. This time these solutions are not working for me, but maybe I am missing something. Here's my code:
.h file
#ifndef TABLE1_H
#define TABLE1_H
#include <cstdlib> // Provides size_t
namespace main_savitch_12A
{
template <class RecordType>
class table
{
public:
// MEMBER CONSTANT -- See Appendix E if this fails to compile.
static const std::size_t CAPACITY = 811;
// CONSTRUCTOR
table( );
// MODIFICATION MEMBER FUNCTIONS
void insert(const RecordType& entry);
void remove(int key);
// CONSTANT MEMBER FUNCTIONS
bool is_present(int key) const;
void find(int key, bool& found, RecordType& result) const;
std::size_t size( ) const { return used; }
private:
// MEMBER CONSTANTS -- These are used in the key field of special records.
static const int NEVER_USED = -1;
static const int PREVIOUSLY_USED = -2;
// MEMBER VARIABLES
RecordType data[CAPACITY];
std::size_t used;
// HELPER FUNCTIONS
std::size_t hash(int key) const;
std::size_t next_index(std::size_t index) const;
void find_index(int key, bool& found, std::size_t& index) const;
bool never_used(std::size_t index) const;
bool is_vacant(std::size_t index) const;
};
}
#include "table1.template" // Include the implementation.
#endif
.template file
template<class RecordType>
table<RecordType>::table(){
used = 32;
}
main file
#include <stdio.h>
#include "table1.h"
int main(int argc, char **argv)
{
printf("hello world\n");
return 0;
}
My template and my .h files are called table1. The error I am getting when I run the program is in the template file. It reads: "table does not name a type" How can I fix this issue?
In your template implementation your are missing the namespace, use this:
template <class RecordType>
main_savitch_12A::table<RecordType>::table()
{
used = 32;
};

Problems accessing private data members c++

I have three files: Stack.cc, Stack.h and stacktest.cc . I am not sure about which files to include where, and i am getting different errors because of it. Currently, the code from Stack.h is:
#ifndef STACK_H
#define STACK_H
#include<iostream>
using namespace std;
template<typename T>
class Stack
{
public:
Stack();
void push(int);
void pop();
int top();
int size();
bool empty();
private:
class Element
{
public:
int data;
Element *next;
Element(Element *n, T d) : next{n}, data{d} {}
};
Element *first;
int num;
};
#endif
#include"Stack.cc"
the (relevant, i think) code from Stack.cc is:
#include<iostream>
using namespace std;
template<typename T>
Stack<T>::Stack()
{
first=nullptr;
}
template<typename T>
void Stack<T>::push(int)
{
num++;
first = new Element(first, data);
}
Stacktest is currently just a test file attempting to call the default constructor. The errors i currently get are:
In file included from Stack.h:30:0,
from stacktest.cc:2:
Stack.cc: In member function ‘void Stack<T>::push(int)’:
Stack.cc:22:28: error: ‘data’ was not declared in this scope
first = new Element(first, data);
^
Stack.cc: In function ‘int size()’:
Stack.cc:62:11: error: ‘num’ was not declared in this scope
return num;
For some reason it wont let me access private data members. Before i didnt have the include in the .h file and instead included the .h in Stack.cc, and that worked, although wouldnt let me access the stack class from Stacktest.cc(Stacktest.cc just includes Stack.h)
Any help would be greatly appreciated, thanks.

Error in creating template class

I found this vector template class implementation, but it doesn't compile on XCode.
Header file:
// File: myvector.h
#ifndef _myvector_h
#define _myvector_h
template <typename ElemType>
class MyVector
{
public:
MyVector();
~MyVector();
int size();
void add(ElemType s);
ElemType getAt(int index);
private:
ElemType *arr;
int numUsed, numAllocated;
void doubleCapacity();
};
#include "myvector.cpp"
#endif
Implementation file:
// File: myvector.cpp
#include <iostream>
#include "myvector.h"
template <typename ElemType>
MyVector<ElemType>::MyVector()
{
arr = new ElemType[2];
numAllocated = 2;
numUsed = 0;
}
template <typename ElemType>
MyVector<ElemType>::~MyVector()
{
delete[] arr;
}
template <typename ElemType>
int MyVector<ElemType>::size()
{
return numUsed;
}
template <typename ElemType>
ElemType MyVector<ElemType>::getAt(int index)
{
if (index < 0 || index >= size()) {
std::cerr << "Out of Bounds";
abort();
}
return arr[index];
}
template <typename ElemType>
void MyVector<ElemType>::add(ElemType s)
{
if (numUsed == numAllocated)
doubleCapacity();
arr[numUsed++] = s;
}
template <typename ElemType>
void MyVector<ElemType>::doubleCapacity()
{
ElemType *bigger = new ElemType[numAllocated*2];
for (int i = 0; i < numUsed; i++)
bigger[i] = arr[i];
delete[] arr;
arr = bigger;
numAllocated*= 2;
}
If I try to compile as is, I get the following error:
"Redefinition of 'MyVector::MyVector()'"
The same error is displayed for every member function (.cpp file).
In order to fix this, I removed the '#include "myvector.h"' on the .cpp file, but now I get a new error:
"Expected constructor, destructor, or type conversion before '<' token".
A similar error is displayed for every member as well.
Interestingly enough, if I move all the .cpp code to the header file, it compiles fine. Does that mean I can't implement template classes in separate files?
It's always a good idea to place your templates in a header file. That way you don't mess up the linker with multiple definitions of the same instantiations and such.
And of course there's the circular inclusion :).
First, you have
#include "myvector.cpp"
which creates a circular reference between the files. Just get rid of it.
The other problem is that you are defining a template class inside a .cpp file. Template definitions are only allowed inside header files. There may be ways around that, but for g++ (which XCode uses) that's how the cookie crumbles.