Problem with templates and inheritance

D

Der Andere

My assignment is to derive several container classes from class Node. When I
try to compile the following program, the compiler gives the following:
error C2440: '=' : cannot convert from 'class Node<int,1> *' to 'class
SLNode<int> *'
The error relates to the expression temp=temp->succ[0] in the function
writePath() and I do not know what to do about it.


#include <iostream>

using namespace std;

template<typename Info, int Arity>
class Node
{
protected:
Info info;
Node *succ[Arity];
public:
Node (const Info& i) : info(i)
{for (int j = 0; j < Arity; j++)
succ[j] = NULL;
};
};


// ------- Singly linked list -------
// ----------------------------------
template<typename Info>
class SLNode : public Node<Info,1>
{
public:
SLNode<Info> (const Info& i) : Node<Info,1>(i) {}
void append (const Info& i);
bool contains (const Info& i);
SLNode remove(const Info& i);

void writePath() // Später entfernen
{
for (SLNode *temp=this; temp->succ!=NULL; temp = temp->succ[0])
cout << info << " -> ";
cout << endl;
}

};

template<typename Info>
void SLNode<Info> :: append (const Info& i)
{
for (SLNode *temp=this; temp->succ!=NULL; temp=temp->succ);
temp->succ = new SLNode(i);
};

template<typename Info>
bool SLNode<Info> :: contains(const Info &i)
{
for (SLNode *temp=this; temp!=NULL; temp=temp->succ)
if (temp->info==i) return true;
return false;
}

template<typename Info>
SLNode<Info> SLNode<Info> :: *remove(const Info &i)
{
SLNode *before,*after;
if (i==*this) // Calling object has to be deleted
{
after = this->succ;
delete this;
return after;
}
for (*before=this; before->succ!=&i; before=before->succ)
if (before==NULL) return this; // Element does not exist
*after = (before->succ)->succ;
delete before->succ;
before->succ = after;
return this;
}

void main()
{
int nr=0;
SLNode<int> *SLNode1 = new SLNode<int>(10);
SLNode1->writePath();
// for (int i=1; i<10; i++)
// SLNode1.append(i);
}


BTW, is there an easier way to instantiate SLNode than the way I did it (it
really looks awkward)? I would prefer to have an instance of SLNode<int>
initialized with a certain value (say 10), not merely a pointer.

I would like SLNode to have the member variable succ instead of a somewhat
non-sensical succ[1]. Is the only way to achieve this to declare a new
member variable succ?

Thanks.

Regards,
Matthias
 
R

Rolf Magnus

Der said:
My assignment is to derive several container classes from class Node.
When I try to compile the following program, the compiler gives the
following: error C2440: '=' : cannot convert from 'class Node<int,1>
*' to 'class SLNode<int> *'
The error relates to the expression temp=temp->succ[0] in the function
writePath() and I do not know what to do about it.


#include <iostream>

using namespace std;

template<typename Info, int Arity>
class Node
{
protected:
Info info;
Node *succ[Arity];
public:
Node (const Info& i) : info(i)
{for (int j = 0; j < Arity; j++)
succ[j] = NULL;
};
};


// ------- Singly linked list -------
// ----------------------------------
template<typename Info>
class SLNode : public Node<Info,1>
{
public:
SLNode<Info> (const Info& i) : Node<Info,1>(i) {}
void append (const Info& i);
bool contains (const Info& i);
SLNode remove(const Info& i);

void writePath() // Später entfernen
{
for (SLNode *temp=this; temp->succ!=NULL; temp = temp->succ[0])

temp is of type SLNode<Info>* and temp->succ[0] is of type
Node<Info,1>*, which is the base class. You cannot implicitly convert
from pointer to a base class into a pointer to derived because such a
conversion is considered unsafe. You have to use a cast, like:

for (SLNode *temp=this; temp->succ!=NULL;
temp = static_cast said:
cout << info << " -> ";
cout << endl;
}

};

template<typename Info>
void SLNode<Info> :: append (const Info& i)
{
for (SLNode *temp=this; temp->succ!=NULL; temp=temp->succ);
temp->succ = new SLNode(i);
};

template<typename Info>
bool SLNode<Info> :: contains(const Info &i)
{
for (SLNode *temp=this; temp!=NULL; temp=temp->succ)
if (temp->info==i) return true;
return false;
}

template<typename Info>
SLNode<Info> SLNode<Info> :: *remove(const Info &i)

This must be:

{
SLNode *before,*after;
if (i==*this) // Calling object has to be deleted
{
after = this->succ;
delete this;
return after;
}
for (*before=this; before->succ!=&i; before=before->succ)
^
leave out that *.
if (before==NULL) return this; // Element does not exist
*after = (before->succ)->succ;
delete before->succ;
before->succ = after;
return this;
}

void main()

main must return int in C++.
{
int nr=0;
SLNode<int> *SLNode1 = new SLNode<int>(10);
SLNode1->writePath();
// for (int i=1; i<10; i++)
// SLNode1.append(i);

Your object is not properly destroyed. For every use of new, there
should be a delete. So add:

delete SLNode1;
}


BTW, is there an easier way to instantiate SLNode than the way I did
it (it really looks awkward)? I would prefer to have an instance of
SLNode<int> initialized with a certain value (say 10), not merely a
pointer.

Uhm, it's you who chose to use a pointer and dynamic allocation. If you
don't want that, then just don't do it. Write:

SLNode said:
I would like SLNode to have the member variable succ instead of a
somewhat non-sensical succ[1]. Is the only way to achieve this to
declare a new member variable succ?

You could also write *succ instead of succ[0], but that's not much
better.
 
M

Michiel Salters

Der Andere said:
My assignment is to derive several container classes from class Node.
When I try to compile the following program, the compiler gives the
following: error C2440: '=' : cannot convert
from 'class Node<int,1> *' to 'class SLNode<int> *'
The error relates to the expression temp=temp->succ[0] in the function
writePath() and I do not know what to do about it.

Correct. The templates don't help readability, so I'll strip the example
down. You could have done that yourself, BTW.

class Base {
protected:
Base* succ;
}
class Derived {
void foo() {
Derived* temp = this->succ;
}
}
This fails, because this->succ might point to AnotherDerived object,
instead of the expected Derived object. Base::succ doesn't have the
correct type.

The hack is to use dynamic_cast< >, which probably means that
Base::~Base must be virtual (or another function, you need RTTI).
In this case, the Base*->Derived* conversion check is delayed to
runtime.

Th neater solution is the CRTP pattern by Coplien:

template <typename Derived> class Base
{
protected:
Derived* succ;
}
class Derived : public Base<Derived> {
//...
};

Regards,
Michiel Salters
 
D

Der Andere

My assignment is to derive several container classes from class Node.
When I try to compile the following program, the compiler gives the
following: error C2440: '=' : cannot convert from 'class Node<int,1>
*' to 'class SLNode<int> *'
The error relates to the expression temp=temp->succ[0] in the function
writePath() and I do not know what to do about it.


#include <iostream>

using namespace std;

template<typename Info, int Arity>
class Node
{
protected:
Info info;
Node *succ[Arity];
public:
Node (const Info& i) : info(i)
{for (int j = 0; j < Arity; j++)
succ[j] = NULL;
};
};


// ------- Singly linked list -------
// ----------------------------------
template<typename Info>
class SLNode : public Node<Info,1>
{
public:
SLNode<Info> (const Info& i) : Node<Info,1>(i) {}
void append (const Info& i);
bool contains (const Info& i);
SLNode remove(const Info& i);

void writePath() // Später entfernen
{
for (SLNode *temp=this; temp->succ!=NULL; temp = temp->succ[0])

temp is of type SLNode<Info>* and temp->succ[0] is of type
Node<Info,1>*, which is the base class. You cannot implicitly convert
from pointer to a base class into a pointer to derived because such a
conversion is considered unsafe. You have to use a cast, like:

for (SLNode *temp=this; temp->succ!=NULL;
temp = static_cast<SLNode*>(temp->succ[0]))

Looks really ugly ;-) (though perfectly understandable)
cout << info << " -> ";
cout << endl;
}

};

template<typename Info>
void SLNode<Info> :: append (const Info& i)
{
for (SLNode *temp=this; temp->succ!=NULL; temp=temp->succ);
temp->succ = new SLNode(i);
};

template<typename Info>
bool SLNode<Info> :: contains(const Info &i)
{
for (SLNode *temp=this; temp!=NULL; temp=temp->succ)
if (temp->info==i) return true;
return false;
}

template<typename Info>
SLNode<Info> SLNode<Info> :: *remove(const Info &i)

This must be:

SLNode<Info>* SLNode<Info> :: remove(const Info &i)

Oops.
{
SLNode *before,*after;
if (i==*this) // Calling object has to be deleted
{
after = this->succ;
delete this;
return after;
}
for (*before=this; before->succ!=&i; before=before->succ)
^
leave out that *.
if (before==NULL) return this; // Element does not exist
*after = (before->succ)->succ;
delete before->succ;
before->succ = after;
return this;
}

void main()

main must return int in C++.
{
int nr=0;
SLNode<int> *SLNode1 = new SLNode<int>(10);
SLNode1->writePath();
// for (int i=1; i<10; i++)
// SLNode1.append(i);

Your object is not properly destroyed. For every use of new, there
should be a delete. So add:

delete SLNode1;
}


BTW, is there an easier way to instantiate SLNode than the way I did
it (it really looks awkward)? I would prefer to have an instance of
SLNode<int> initialized with a certain value (say 10), not merely a
pointer.

Uhm, it's you who chose to use a pointer and dynamic allocation. If you
don't want that, then just don't do it. Write:

SLNode said:
I would like SLNode to have the member variable succ instead of a
somewhat non-sensical succ[1]. Is the only way to achieve this to
declare a new member variable succ?

You could also write *succ instead of succ[0], but that's not much
better.

Thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top