C++ template class' inner class access - c++

I have a class like this:
#include <iostream>
template <class T>
class LL
{
using size_t = unsigned int;
class Node
{
T m_data;
Node* m_next;
Node(const T& data) :m_data{ data }, m_next{ nullptr }{}
friend std::ostream& operator<<(std::ostream& out, const Node& node)
{
out << node.m_data;
return out;
}
friend std::ostream& operator<<(std::ostream& out, const LL& ll);
friend class LL;
};
Node* m_first{ nullptr };
size_t m_size{ 0 };
Node* newNode(const T& data)
{
return new Node{ data };
}
public:
void push(const T& data)
{
Node* temp = newNode(data);
temp->m_next = m_first;
m_first = temp;
++m_size;
}
Node* head()
{
return m_first;
}
size_t size() const
{
return m_size;
}
~LL()
{
if (m_first)
{
Node* trav = m_first->m_next;
Node* foll = m_first;
while (trav)
{
delete foll;
foll = trav;
trav = trav->m_next;
}
delete foll;
}
}
friend std::ostream& operator<<(std::ostream& out, const LL& ll)
{
Node* trav = ll.m_first;
while (trav)
{
out << *trav << ' ';
trav = trav->m_next;
}
return out;
}
};
I also have a function template somewhere else below this class in the same file that tries to access Node and looks like this with two compiler errors:
template <typename T>
int getSize(LL<T>::Node* node) //C2065: node is undeclared, C3861: node is not found
{
if (node)
{
return 1 + getSize(node->m_next);
}
return 0;
} //does not compile
After sometime I tried this, again with two compiler:
template <typename T>
int getSize(LL<T>::Node<T>* node) //C2065 like before, C7510: use of dependent template name must be prefixed with 'template'
{
if (node)
{
return 1 + getSize(node->m_next);
}
return 0;
} //does not compile
After sometime again, I tried the below which compiled fine.
template <typename T>
int getSize(typename LL<T>::template Node<T>* node)
{
if (node)
{
return 1 + getSize(node->m_next);
}
return 0;
}
Now, when I tried to call this function from my driver function, I got compiler errors again:
int main()
{
LL<int> ll;
std::cout << getSize(ll.head()); //E0304, C2672 and C2783
//E0304: no instance of the function template "getSize" matches the argument list
//C2672: no matching overload function found
//C2783: could not deduce template argument for 'T'
}
I tried everything that I possible could and couldn't sort this problem out. Could someone please explain me what is going on?
Note: All codes that I've mentioned here are in the same file.

getSize(ll.head()) fails because of non-deduced context; template parameter T can't be deduced automatically.
If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:
The declaration should be
template <typename T>
int getSize(typename LL<T>::Node* node) // using class instead of typename works for OP (MSVC)
{
//some code
}
And since Node is not a template you don't need to use template keyword.
LIVE
See Where and why do I have to put the “template” and “typename” keywords? about why use the keyword typename and template.

Related

Reference to a derived template class object incorrectly passed to overloaded operator<< body

I use Visual Studio 2019 compiler. For educational purposes I have decided to implement/create a template based FIFO queue. To achieve that I decided to inherit earlier implemented and well functioning doubly linked list (template based as well).
Here is the code which I purposefully simplified for presentation clarity purposes (I removed all the insignificant member functions declarations and definitions):
Doubly_linked_list.h :
#pragma once
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class list;
template <class T>
ostream& operator<<(ostream& out, const list<T>& arg);
template <class T>
class list
{
protected:
struct node
{
T* obj_ptr;
node* next_node_ptr;
node* previous_node_ptr;
node()
: next_node_ptr(NULL), previous_node_ptr(NULL), obj_ptr(NULL) {}
};
node* first_node_ptr;
node* last_node_ptr;
node* chosen_node_ptr;
int element_counter;
public:
list()
{
first_node_ptr = last_node_ptr = chosen_node_ptr = NULL;
element_counter = 0;
}
friend ostream& operator<< <T>(ostream& out, const list<T>& arg);
void set_chosen_after_last()
{
chosen_node_ptr = NULL;
}
void add_element(T& obj);
~list();
private:
void add_element_as_first(node* newly_added_node_ptr);
void add_element_as_last(node* newly_added_node_ptr);
void add_element_in_middle(node* newly_added_node_ptr);
};
//##################################################################################
//##################################################################################
template <class T>
list<T>::~list()
{
if (!first_node_ptr)
return;
node* bishop;
int i;
cout << "\n\n";
for (chosen_node_ptr = first_node_ptr, i = 0; chosen_node_ptr; i++)
{
bishop = chosen_node_ptr->next_node_ptr;
delete chosen_node_ptr;
chosen_node_ptr = bishop;
}
}
template <class T>
ostream& operator<<(ostream& out, const list<T>& arg)
{
if (!(arg.first_node_ptr))
return out;
typename list<T>::node* chaser = arg.first_node_ptr;
for (; chaser; chaser = chaser->next_node_ptr)
{
out << *(chaser->obj_ptr) << "\t";
}
return out;
}
template <class T>
void list<T>::add_element_in_middle(node* newly_added_node_ptr)
{
node* previous_node = chosen_node_ptr->previous_node_ptr;
previous_node->next_node_ptr = newly_added_node_ptr;
newly_added_node_ptr->previous_node_ptr = previous_node;
newly_added_node_ptr->next_node_ptr = chosen_node_ptr;
chosen_node_ptr->previous_node_ptr = newly_added_node_ptr;
chosen_node_ptr = newly_added_node_ptr;
}
template <class T>
void list<T>::add_element_as_last(node* newly_added_node_ptr)
{
last_node_ptr->next_node_ptr = newly_added_node_ptr;
newly_added_node_ptr->previous_node_ptr = last_node_ptr;
last_node_ptr = newly_added_node_ptr;
}
template <class T>
void list<T>::add_element_as_first(node* newly_added_node_ptr)
{
if (!first_node_ptr)
{
first_node_ptr = newly_added_node_ptr;
last_node_ptr = newly_added_node_ptr;
}
else
{
newly_added_node_ptr->next_node_ptr = first_node_ptr;
first_node_ptr->previous_node_ptr = newly_added_node_ptr;
first_node_ptr = newly_added_node_ptr;
chosen_node_ptr = first_node_ptr;
}
}
template <class T>
void list<T>::add_element(T& obj)
{
node* new_node_ptr = new node;
new_node_ptr->obj_ptr = &obj;
if (!first_node_ptr || chosen_node_ptr == first_node_ptr)
add_element_as_first(new_node_ptr);
else
{
if (!chosen_node_ptr)
{
add_element_as_last(new_node_ptr);
}
else
{
add_element_in_middle(new_node_ptr);
}
}
element_counter++;
}
Queue.h :
#pragma once
#include "Doubly_linked_list.h"
template <class t>
class queue;
template <class T>
ostream& operator<<(ostream& out, typename const queue<T>& arg);
template <class T>
class queue : private list<T>
{
public:
void push(T arg)
{
list<T>::set_chosen_after_last();
list<T>::add_element(arg);
}
friend ostream& operator<< <> (ostream& out, typename const queue<T>& arg);
};
template <class T>
ostream& operator<<(ostream& out, typename const queue<T>& arg)
{
if (!(arg.list<T>::first_node_ptr))
return out;
typename list<T>::node* chaser = arg.first_node_ptr;
for (; chaser; chaser = chaser->next_node_ptr)
{
out << *(chaser->obj_ptr) << "\t";
}
return out;
}
Main.cpp :
#include <iostream>
#include "Queue.h"
int main()
{
queue<int> ex1;
ex1.push(5);
cout << ex1;
}
As a result of testing the above code I was expecting the number 5 to be printed on the console, the result is however some random integer number (different each time I run this code) like this one:
7599868
I started debugging my code and noticed that arg, being rhs argument for overloaded operator<< and also being the reference to ex1 object, somehow has its obj_ptr pointing at integer object of value different than 5 inside the overloaded operator body. Instead the value is the random one eventually printed on the console as you can see on below screen shot from the debugger mode:
Debugger_mode_screen_shot.png
I am now running out of ideas on the possible root cause of such a behavior. To my knowledge arg should refer to the exact same object in memory as ex1, hence their obj_ptr pointers should point at the exact same object of type T (integer in this case) and of the exact same value (5 in this case). Although this is not what actually happens. I have read through numerous topics here on SO and could not find any of them that would address this particular situation/issue.
What is even more interesting and leading me to a greater confusion is the fact that I have tried to recreate this issue by writing a separate simple code imitating the inheritance mechanisms used in the original code. Here however the results are totally as expected - reference to an inherited template class object is being passed to the overloaded operator correctly (with the correct value of integer object pointed at by ptr_a pointer) and hence the console eventually prints out the correct value:
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class Base;
template <class T>
ostream& operator<<(ostream& out, Base<T>& arg);
template <class T>
class Base
{
protected:
struct node
{
T* ptr_a;
node* next_node_ptr;
node()
{
ptr_a = new T;
next_node_ptr = NULL;
}
};
node* first_node_ptr;
public:
Base()
{
first_node_ptr = new node;
*(first_node_ptr->ptr_a) = 5;
}
friend ostream& operator<< <> (ostream& out, Base<T>& arg);
};
template <class t>
ostream& operator<<(ostream& out, Base<t>& arg)
{
cout << "this is " << *arg.first_node_ptr->ptr_a << endl;
return out;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
template <class T>
class Deriv;
template <class T>
ostream& operator<<(ostream& out, const Deriv<T>& arg);
template <class T>
class Deriv : private Base<T>
{
public:
void new_function() {}
friend ostream& operator<< <> (ostream& out, const Deriv& arg);
};
template <class T>
ostream& operator<<(ostream& out, const Deriv<T>& arg)
{
cout << "This is " << *arg.first_node_ptr->ptr_a << endl;
return out;
}
int main()
{
Base<int> ex1;
cout << ex1;
Deriv<int> ex2;
cout << ex2;
}
Output:
this is 5
This is 5
It is worth mentioning that the overloading operator<< worked perfectly fine for list < T > when I originally implemented it. Also I tried to use Code::Blocks to test this code but the issue persisted there as well. As you can also see I befriended operator<< by first forward declaring it before implementing the actual template class definition (to let the friendship always be constituted for a given type parameter particularly and not for all possible types at once). This method has worked perfectly fine for me in list < T > and in all the previous similar codes.
Can any of you guys point out a possible root cause of this situation and what exactly I should do now to make it work as expected (in this case to print 5 instead of some random number)? Thank you in advance for any ideas.
This code stores a pointer to the object referred to by obj.
template <class T>
void list<T>::add_element(T& obj)
{
node* new_node_ptr = new node;
new_node_ptr->obj_ptr = &obj;
...
This code calls list<T>::add_element with a reference to a local object, namely arg.
void push(T arg)
{
list<T>::set_chosen_after_last();
list<T>::add_element(arg);
}
Once push has exited the pointer stored by add_element is invalid. This explains the garbage value you see when you later use the pointer.
The solution is not to store pointers in your nodes but to copy or move the objects passed to your list and queue functions to your nodes.

Errors with friend function in Clion c++

I have written a small code to practice data structures and the beautiful C++.
The code below works completely fine if make the variables in SNode to public and remove the friend class ... line. However, based on the textbook I'm reading, this should be working fine.
The errors I get are as follow:
line 11: error: ‘SLinkedList’ is not a class template
In instantiation of ‘class SNode’: line 10: error: template argument required for ‘class SLinkedList’
In instantiation of ‘void SLinkedList::addFront(const E&) [with E = int]’: line 9: error: ‘int SNode<int>::elem’ is private
Below is the code written in Clion:
#include <iostream>
using namespace std;
template <typename E>
class SNode {
private:
E elem; //line 9
SNode<E> * next; // line 10
friend class SLinkedList<E>; //Provide SLinkedList access to these private variables (line 11)
};
template <typename E>
class SLinkedList {
public:
SLinkedList();
~SLinkedList();
bool empty() const;
const E& front() const;
void addFront(const E& e);
void removeFront();
void printAll() const;
private:
SNode<E> * head;
};
template <typename E>
SLinkedList<E>::SLinkedList():head(NULL){};
template <typename E>
bool SLinkedList<E>::empty() const {
return (head==NULL);
}
template <typename E>
void SLinkedList<E>::addFront(const E &e) {
SNode<E> * node = new SNode<E>;
node->elem=e;
node->next=head;
head=node;
}
template <typename E>
void SLinkedList<E>::removeFront() {
SNode<E> * temp = head;
head = head->next;
delete temp;
}
template <typename E>
const E& SLinkedList<E>::front() const {
return head->elem;
}
template <typename E>
SLinkedList<E>::~SLinkedList() {
while (!empty()){
removeFront();
}
}
template <typename E>
void SLinkedList<E>::printAll() const {
SNode<E> * itr =head;
while (itr != NULL){
cout<<itr->elem<<" ";
itr = itr->next;
}
}
int main() {
cout << "Hello, World!" << endl;
SLinkedList<int> test ;
test.addFront(2);
test.addFront(3);
test.addFront(6);
test.addFront(8);
test.addFront(19);
test.printAll();
return 0;
}
friend class SLinkedList<E>;
This template is not declared yet. Your C++ code gets compiled in an orderly manner, from the beginning of your source file to the end. Until this template gets declared later on in this header file, the compiler has no clue what this is.
The solution is very simple: add a forward declaration to the beginning of the header file, because the SNode template declaration:
template <typename E> class SLinkedList;
template <typename E>
class SNode {
private:
E elem;
SNode<E> * next;
friend class SLinkedList<E>;
};
friend class SLinkedList<E>;
At this point SLinkedList is not defined yet as a template, hence giving the error. Give a forward declaration for SLinkedList, everything will be fine than.
Add this :-
template <typename E>
class SLinkedList;

a function call changes it's parameters

I have the following code:
#include <iostream>
using namespace std;
template <class T>
class Iterator;
template <class T>
class List;
template <class T>
class List {
public:
struct Node;
Node* first;
friend class Iterator<T>;
List() :
first(NULL) { }
Iterator<T> begin() {
cout << first->data << endl;
return Iterator<T>(*this, first); // <--- problematic call
}
void insert(const T& data) {
Node newNode(data, NULL);
first = &newNode;
}
};
template <class T>
struct List<T>::Node {
private:
T data;
Node* next;
friend class List<T>;
friend class Iterator<T>;
Node(const T& data, Node* next) :
data(data), next(next) { }
};
template <class T>
class Iterator {
private:
const List<T>* list;
typename List<T>::Node* node;
friend class List<T>;
public:
Iterator(const List<T>& list, typename List<T>::Node* node) {
cout << node->data << endl;
}
};
int main() {
List<int> list;
list.insert(1);
list.begin();
return 0;
}
First I set the node data to "1" (int). Ater that I just pass it to the Iterator constructor, but it changes the value of node->data.
I printed node->data before and after the call:
1
2293232
I guess that 2293232 is an address of something, but I can't find the reason this happens.
When you write
void insert(const T& data) {
Node newNode(data, NULL);
first = &newNode;
}
Then:
You create an object on the stack
Point some (more) persistent pointer to its address
Destruct it as it goes out of scope
So you're left with garbage stuff.

Template LinkedList class with nested class

I'm trying to implement a linked list with a template class LList and Nested Iterator and Node classes. Here's the code:
template <typename T1>
class LList
{
public:
class Iterator
{
public:
Iterator();
T1 get() const;
void next();
void previous();
bool equals(Iterator iter) const;
private:
Node* position;
LList* container;
};
LList();
~LList();
void pushBack(T1 data);
Iterator begin();
Iterator end();
void insert (Iterator iter, T1 data);
Iterator erase(Iterator Iter);
private:
class Node
{
public:
Node(T1 data);
private:
T1 data;
Node* ptr_next;
Node* ptr_prev;
};
Node* ptr_first;
Node* ptr_last;
};
template <typename T1>
LList<T1>::Node::Node(T1 data)
{
this->data = data;
ptr_next = 0;
ptr_prev =0;
}
template <typename T1>
LList<T1>::Iterator::Iterator()
{
position = 0;
container = 0;
}
template <typename T1>
T1 LList<T1>::Iterator::get() const
{
return position->data;
}
template <typename T1>
void LList<T1>::Iterator::next()
{
if(position == container->ptr_last)
{
position = container->ptr_first;
}
else
{
position = position->ptr_next;
}
}
template <typename T1>
void LList<T1>::Iterator::previous()
{
if(!position)
{
position = container->ptr_last;
}
else
{
position = position->ptr_prev;
}
}
template <typename T1>
bool LList<T1>::Iterator::equals(Iterator iter) const
{
return position == iter.position;
}
template <typename T1>
LList<T1>::LList()
{
ptr_first = 0;
ptr_last = 0;
}
template <typename T1>
LList<T1>::~LList()
{
while (ptr_first)
{
Node* tmp = ptr_first;
ptr_first = ptr_first->ptr_next;
delete tmp;
tmp = 0;
}
}
template <typename T1>
void LList<T1>::pushBack(T1 data)
{
Node* new_node = new Node(data);
if(ptr_first==0)
{
ptr_first = new_node;
ptr_last = new_node;
}
else
{
ptr_last->ptr_next = new_node;
new_node->ptr_prev = ptr_last;
ptr_last = new_node;
}
}
template <typename T1>
Iterator LList<T1>::begin()
{
Iterator iter;
iter.positon = ptr_first;
iter.container = this;
return iter;
}
template <typename T1>
Iterator LList<T2>::end()
{
Iterator iter;
iter.position = ptr_last;
iter.container = this;
return iter;
}
template <typename T1>
void LList<T1>::insert(Iterator iter, T1 data)
{
if (iter.position == 0)
{
pushBack(data);
return;
}
Node* before;
Node* after;
after = iter.position;
before = iter.position->ptr_prev;
Node* new_node = new Node(data);
after->ptr_prev = new_node;
if (before == 0) ptr_first = new_node;
else before->ptr_next = new_node;
new_node->ptr_prev = before;
new_node->ptr_next = after;
}
template <typename T1>
Iterator LList<T1>::erase(Iterator iter)
{
Node* after = iter.position->ptr_next;
Node* before = iter.position->ptr_prev;
Node* remove = iter.position;
if (remove == ptr_first) ptr_first = after;
else before->ptr_next = after;
if (remove == ptr_last) ptr_last = before;
else after->ptr_prev = before;
delete remove;
remove = 0;
}
I've seen how do it without nested classes but I need to do it with a nested class.
Any help on why it doesn't compile will help :) Thanks.
Well, Iterator is the name of a nested class, so when you use it in the definition of a member function of your LList class template as the return type, you have to fully qualify it (and add the typename disambiguator to tell the compiler that what follows the :: shall be parsed as the name of a type).
For instance:
template <typename T1>
typename LList<T1>::Iterator LList<T1>::erase(Iterator iter)
// ^^^^^^^^^^^^^^^^^^^^
There are several instances of this error, so you will have to fix all of them.
You are also referring to class Node before its definition appears inside LList. Therefore, you should have a forward declaration for it:
template <typename T1>
class LList
{
class Node;
// ^^^^^^^^^^^
// Forward declaration for Node
public:
// ...
class Iterator
{
// ...
Node* position; // <== Now this is OK because of the forward declaration
// ...
};
There are different error :
The first is that you use Node into the Iterator class, but you need to declare Node before it :
template <typename T1>
class LList
{
private:
class Node
{
public:
Node(T1 data);
private:
T1 data;
Node* ptr_next;
Node* ptr_prev;
};
Node* ptr_first;
Node* ptr_last;
public:
LList();
~LList();
void pushBack(T1 data);
class Iterator
{
public:
Iterator();
T1 get() const;
void next();
void previous();
bool equals(Iterator iter) const;
private:
Node* position;
LList* container;
};
Iterator begin();
Iterator end();
void insert (Iterator iter, T1 data);
Iterator erase(Iterator Iter);
};
That is the first thing.
The second is that Iterator is a nested type, so you need to be more specific when you return un object of this type :
template <typename T1>
typename LList<T1>::Iterator LList<T1>::begin()
And the last error is :
template <typename T1>
Iterator LList<T2>::end()
Here is the correct lign :
template <typename T1>
typename LList<T1>::Iterator LList<T1>::end()
// ^^^^^^^^^^^^^^^^^ ^^

stack template doesn't compile push func

I wrote this something-like-stack data structure:
template<class T>
class Stos {
class Element {
public:
T n;
Element* prev;
Element(const T& k = 0): n(k), prev(0) {}
};
Element* member;
Stos(Stos&);
public:
Stos(const T&);
~Stos();
unsigned int count;
T get();
Element* push(const T&);
T pop();
void mod(const T&);
};
And implementation (same file):
template<class T>
Stos<T>::Stos(const T& n = 0): count(1) {
member = new Element(n);
}
template<class T>
T Stos<T>::get() {
return member->n;
}
template<class T>
Stos<T>::Element* Stos<T>::push(const T& n = 0) {
Element* point = member;
member = new Element;
member->prev = point;
if(n != 0) member->n = n;
++count;
return member;
}
template<class T>
T Stos<T>::pop() {
Element* point = member;
T n = point->n;
member = point->prev;
--count;
delete point;
return n;
}
template<class T>
void Stos<T>::mod(const T& n) {
member->n = n;
}
template<class T>
Stos<T>::~Stos() {
while(member) pop();
}
And when I try to compile it with g++, I get this error about the first line of definition of Stos::Element* Stos::push(): expected constructor, destructor, or type conversion before ‘*’ token. It is my first try to write something with templates. This stack code did work without templates, when I'd edited it, then I got the error, everything worked just fine before with "int" everywhere instead of "T".
And I can't find out why it doesn't compile. Can't I use pointer to class::member?
You need to prefix the name Element with typename
typename Stos<T>::Element* Stos<T>::push(const T& n = 0)
Here's a link to a full explanation of why this is necessary
http://pages.cs.wisc.edu/~driscoll/typename.html
You should also consider using
const T &n = T()
instead of
const T &n = 0
Since not all possible T may be able to be initialized from 0!