Compilation problem with templates

J

Jerome Durand

Hello,

I'm trying to write something along the following lines
but I cannot get this to compile.


template <typename derived> struct Base {
typedef typename derived::valueType valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<CharValue>{
typedef char valueType ;
valueType Value() {return 'a';}

};

struct IntValue: Base<IntValue> {
typedef int valueType ;
valueType Value() {return 1234;}
};

The compiler outputs (first error only):
Error 1: 'valueType' : is not a member of CharValue'


Could someone tell me what is wrong with this?

Thanks
Jerome
 
V

Victor Bazarov

Jerome said:
Hello,

I'm trying to write something along the following lines
but I cannot get this to compile.


template <typename derived> struct Base {
typedef typename derived::valueType valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<CharValue>{
typedef char valueType ;
valueType Value() {return 'a';}

};

struct IntValue: Base<IntValue> {
typedef int valueType ;
valueType Value() {return 1234;}
};

The compiler outputs (first error only):
Error 1: 'valueType' : is not a member of CharValue'


Could someone tell me what is wrong with this?

It can be that the compiler is trying to resolve 'derived::valueType'
too soon (at the time when it encounters the 'typedef' in 'Base', the
'derived' is not completely defined yet. The behaviour of compilers
in this matter can differ somewhat, but the most concervative will
likely choke because 'derived' is incomplete by the time 'Base'
instantiation is attemtped. You might consider replacing the 'typedef'
dance with normal type processing:

template<typename valueType> struct Base {
virtual valueType Value() = 0;
};

struct CharValue : Base<char> {
char Value() { return 'a'; }
};

...

V
 
J

Jerome Durand

Victor Bazarov a écrit :
It can be that the compiler is trying to resolve 'derived::valueType'
too soon (at the time when it encounters the 'typedef' in 'Base', the
'derived' is not completely defined yet. The behaviour of compilers
in this matter can differ somewhat, but the most concervative will
likely choke because 'derived' is incomplete by the time 'Base'
instantiation is attemtped. You might consider replacing the 'typedef'
dance with normal type processing:

template<typename valueType> struct Base {
virtual valueType Value() = 0;
};

struct CharValue : Base<char> {
char Value() { return 'a'; }
};

...

V

This is not really an option because I intend to have
several functions declared in base with different return
types which should depend on types defined in derived...

Jerome
 
V

Victor Bazarov

Jerome said:
Victor Bazarov a écrit :

This is not really an option because I intend to have
several functions declared in base with different return
types which should depend on types defined in derived...

Then you're SOL since those types are members of the class which
is not going to be fully defined by the time Base is instantiated.
Catch 22.

V
 
J

Jerome Durand

Victor Bazarov a écrit :
Then you're SOL since those types are members of the class which
is not going to be fully defined by the time Base is instantiated.
Catch 22.

V

Thank you Victor.

I did something a little differently to break the incomplete definition
dependency... Could have done it without an intermediate class, but I
felt it was better like this:


template <class aderived, class avaluetype> struct DerivedDescription {
typedef aderived derived;
typedef avaluetype valueType;

};
template <typename derivedDescription> struct Base{
typedef typename derivedDescription::valueType valueType;
virtual valueType Value() = 0;
};


struct CharValue: Base< DerivedDescription<CharValue, char> >{
typedef char valueType ;
valueType Value() {return 'a';}
};

struct IntValue: Base<DerivedDescription<IntValue, int>>{
typedef int valueType ;
valueType Value() {return 1234;}
};


Jerome
 
M

mlimber

Hello,

I'm trying to write something along the following lines
but I cannot get this to compile.

template <typename derived> struct Base {
typedef typename derived::valueType valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<CharValue>{
typedef char valueType ;
valueType Value() {return 'a';}

};

struct IntValue: Base<IntValue> {
typedef int valueType ;
valueType Value() {return 1234;}
};

The compiler outputs (first error only):
Error 1: 'valueType' : is not a member of CharValue'

Could someone tell me what is wrong with this?

You're effectively trying to use CharValue and IntValue before they're
defined. Why not simplify it like this:

template <typename T> struct Base {
typedef T valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<char>{
valueType Value() {return 'a';}

};

struct IntValue: Base<int> {
valueType Value() {return 1234;}
};

Cheers! --M
 
M

mlimber

I'm trying to write something along the following lines
but I cannot get this to compile.

template <typename derived> struct Base {
typedef typename derived::valueType valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<CharValue>{
typedef char valueType ;
valueType Value() {return 'a';}

};

struct IntValue: Base<IntValue> {
typedef int valueType ;
valueType Value() {return 1234;}
};

The compiler outputs (first error only):
Error 1: 'valueType' : is not a member of CharValue'

Could someone tell me what is wrong with this?

You're effectively trying to use CharValue and IntValue before they're
defined. Why not simplify it like this:

template <typename T> struct Base {
typedef T valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<char>{
valueType Value() {return 'a';}

};

struct IntValue: Base<int> {
valueType Value() {return 1234;}
};

Cheers! --M
 
J

Jerome Durand

mlimber a écrit :
You're effectively trying to use CharValue and IntValue before they're
defined. Why not simplify it like this:

template <typename T> struct Base {
typedef T valueType;
virtual valueType Value() = 0;
};

struct CharValue: Base<char>{
valueType Value() {return 'a';}

};

struct IntValue: Base<int> {
valueType Value() {return 1234;}
};

Cheers! --M

because this is a trimmed down version of my problem,
where there's more than a single function and hence
more than a single return type required.

-J-
 
J

James Kanze

I'm trying to write something along the following lines
but I cannot get this to compile.
template <typename derived> struct Base {
typedef typename derived::valueType valueType;
virtual valueType Value() = 0;
};
struct CharValue: Base<CharValue>{
typedef char valueType ;
valueType Value() {return 'a';}
};
struct IntValue: Base<IntValue> {
typedef int valueType ;
valueType Value() {return 1234;}
};
The compiler outputs (first error only):
Error 1: 'valueType' : is not a member of CharValue'
Could someone tell me what is wrong with this?

The error message could be clearer (mentionning e.g. incomplete
class), but if you think about how templates and class
definitions work, it should be obvious that this is impossible.
The use of the template as a base class triggers instantiation,
since a class must be complete to be used as a base, and
(§14.7.1/4) "A class template specialization is implicitly
instantiated if the class type is used in a context that
requires a completelydefined object type or if the completeness
of the class type might affect the semantics of the program."
(It's logical, really... what good would implicit instantiation
be otherwise.) In the template, derived::valueType is a
dependant name (obviously), so is resolved at the point of
instantiation. And (§14.6.4.1/3) "For a class template
specialization, [...] the point of instantiation for such a
specialization immediately precedes the namespace scope
declaration or definition that refers to the specialization."
Again, this is the only logical solution: you can't instantiate
(define) a class in the middle of your declaration, so it has to
be either before or after, and if it is after, you still don't
have a complete class to use as a base. Of course, if the point
of instantiation is before the definition of the class which
derives from the template, the contents of that class are not
accessible in the template.

Your basic problem is that you have created a cyclic
dependency. As written, Base can only be instantiated on a
complete class (since it uses contents of the class). And you
need an already instantiated Base in order to define the derived
class. The only solution is to break the cycle: make Base
independant of the derived class. In this case, for example,
you might pass valueType as a template argument, e.g.:

template< typename ValueType > struct Base {
virtual ValueType value() = 0 ;
} ;

struct CharValue : Base< char > { /* ... */ } ;
// etc.

If in your real code, this entails two or three parameters to
Base, so be it. If the number of parameters starts really
getting out of hand, you might also consider using traits. This
does make using Base somewhat more complicated, but it allows
considerably more flexibility.
 
J

Jerome Durand

James Kanze a écrit :
I'm trying to write something along the following lines
but I cannot get this to compile.
template <typename derived> struct Base {
typedef typename derived::valueType valueType;
virtual valueType Value() = 0;
};
struct CharValue: Base<CharValue>{
typedef char valueType ;
valueType Value() {return 'a';}
};
struct IntValue: Base<IntValue> {
typedef int valueType ;
valueType Value() {return 1234;}
};
The compiler outputs (first error only):
Error 1: 'valueType' : is not a member of CharValue'
Could someone tell me what is wrong with this?

The error message could be clearer (mentionning e.g. incomplete
class), but if you think about how templates and class
definitions work, it should be obvious that this is impossible.
The use of the template as a base class triggers instantiation,
since a class must be complete to be used as a base, and
(§14.7.1/4) "A class template specialization is implicitly
instantiated if the class type is used in a context that
requires a completelydefined object type or if the completeness
of the class type might affect the semantics of the program."
(It's logical, really... what good would implicit instantiation
be otherwise.) In the template, derived::valueType is a
dependant name (obviously), so is resolved at the point of
instantiation. And (§14.6.4.1/3) "For a class template
specialization, [...] the point of instantiation for such a
specialization immediately precedes the namespace scope
declaration or definition that refers to the specialization."
Again, this is the only logical solution: you can't instantiate
(define) a class in the middle of your declaration, so it has to
be either before or after, and if it is after, you still don't
have a complete class to use as a base. Of course, if the point
of instantiation is before the definition of the class which
derives from the template, the contents of that class are not
accessible in the template.

Your basic problem is that you have created a cyclic
dependency. As written, Base can only be instantiated on a
complete class (since it uses contents of the class). And you
need an already instantiated Base in order to define the derived
class. The only solution is to break the cycle: make Base
independant of the derived class. In this case, for example,
you might pass valueType as a template argument, e.g.:

template< typename ValueType > struct Base {
virtual ValueType value() = 0 ;
} ;

struct CharValue : Base< char > { /* ... */ } ;
// etc.

If in your real code, this entails two or three parameters to
Base, so be it. If the number of parameters starts really
getting out of hand, you might also consider using traits. This
does make using Base somewhat more complicated, but it allows
considerably more flexibility.

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



all of this sounds very sensible. I don't have the standard, from
which I could have read where the point of instantiation for the
template is required to be, and it does make sense.

As you guessed, my real code needs more than a single parameter,
I finally found a solution on my own, which I find more tedious,
but works. I'll check how traits can be of use here...

I now have a solution to my problem and an explanation for it.
The latter is more precious than the former, it should later
help me to clear away from the same mistake in the first place.

Thank you James!


Jerome
 

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

Staff online

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,816
Latest member
SapanaCarpetStudio

Latest Threads

Top