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