Click here to Skip to main content
15,910,083 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have more classes, each defines its own enum. I need classes to derive from the same template which defines some methods operating with that enum. Why does not this compile?
template<class T> class C1
{
	typename T::CENUM e;
};

class C2: public C1<C2>
{
public:
	typedef enum { A,B } CENUM;
};

...
C2 c2;
...


<br />
error C2039: 'CENUM' : is not a member of 'C2'<br />


thank you.
Posted

That does not compile because you're trying to create a version of C1using C2 at a point in (compilation) time when C2 is not yet known (as it is still being defined).

One way to get around this would be to extract the CENUM from C2 and pass that to C1's template instead:


C++
template<class T> class C1
{
	typename T e;
};


typedef enum { A,B } C2ENUM;
class C2 : public C1<C2ENUM>
{
public:
};



Further, having a class hierarchy where B derives from a version of A where A has knowledge of B (as in your example) has a weird code smell, it seems like a overly complicated way of achieving something.

Hope this helps,
Fredrik
 
Share this answer
 
Comments
Aescleal 9-Jul-10 12:17pm    
It may look weird but the technique the poster is using is a well known implementation pattern in C++. Jim Coplien first publicised it sometime in the 90s in the C++ report and Barton and Nackman used something like it in their textbook from the 90s.

It's very popular in when you want something like overiding but bound at compile time. Have a search for "curiously recurring template pattern" for more information.

Ash
rrrado 10-Jul-10 7:51am    
Well I know my code does not look standard, but AFAIK using class being declared as parameter of parent template is widely used in WTL and other libraries. As sauro wrote static_cast<derived*>(this)->identifier(); works, then why not type declarations?

I have various types of grid controls in application, each grid has it's own class and columns declared in enumeration. I want to keep enum inside class, because it is used only by that class and to avoid many enums at global level. There is lot of code (more methods) which is similar to each grid so I want to use templates to define it. One of such generic method is for example int GetColumnPos(T::enumColumn col) which accepts column identification. Yes it could be declared as int, but type checking would be weak and it would not be so nice and self-explaining.
Aescleal 11-Jul-10 1:48am    
Added an idea to my answer that might help. Give it a look, if it doesn't do what you need then nothing ventured, nothing gained.
rrrado 11-Jul-10 6:44am    
Well I'm afraid I'll have to declare enum outside, thank you.
It does not compile because you have introduced a circular dependency: C1 is a template class that depends on C2, and C2 is a class derived from C1.

What you can do is to declare your enumerations out of each class, then use them as template parameter, like in the following example:

C++
template<class T> class C1
{
   T e;

   ...
};

typedef enum { A, B } CENUM;

class C2: public C1<CENUM>
{
   ...
};

...

C2 instance;

...


Another option is to create a template that inherits from its template argument, as follow:

C++
template<class T> class C1: public T
{
   typename T::CENUM e;

   // C1 inherits all methods from T and add functionalities to it
   ...
};

class C2
{
public:
   typedef enum { A, B } CENUM;

   ...
};

...
C1<C2> instance;
...
 
Share this answer
 
v3
CRTP doesn't really work for types - I've never worked out why to my own satisfaction. You can get around the problem by wrapping up the identifier in a function:

template <class derived> class base
{
    private:
        unsigned identifier()
        {
            return static_cast<derived*>(this)->identifier();
        }
};

class derived : base<derived>
{
    private:
        unsigned identifier()
        {
            return 37;
        }
};


Then instead of switching/branching on the value in the enum in the base class you can switch on the result of the identifier() function. Of course as you want a variable state you won't be able to use a constant as I have in the example but you can introduce complications to taste.

Cheers,

Ash

PS: The return value from the function doesn't have to be an int, it can be just about anything that makes sense in the context of the base class.

PPS: One of the things you said you wanted was to be able to write:

base::function( derived::type t );

and have things routed to the right place. You can do that with a template member function:

#include <iostream>

template<class derived>
class base
{
    public:
        template <typename T>
        int return_value_for_squared( T t )
        {
            int n = static_cast<derived *>(this)->return_value_for( t );
            return n * n;
        }
};

class derived : public base<derived>
{
    public:
        enum values { A, B, C };

        int return_value_for( values v )
        {
            return v;
        }
};

int main()
try
{
    derived d;
    std::cout << d.return_value_for_squared( derived::C ) << std::endl;
}
catch( std::exception &e )
{
    std::cout << "Something went wrong: " << e.what() << std::endl;
}
catch( ... )
{
    std::cout << "Something went wrong, no idea what!" << std::endl;
}
 
Share this answer
 
v3
Comments
rrrado 10-Jul-10 8:07am    
The problem is that I need that enum type as type of parameter of some methods, for exaple
int base<derived>::GetColumnPos(derived::CENUM e)
Aescleal 10-Jul-10 16:25pm    
Can't help you there, I've only seen the pattern you're using successfully applied to functions that act as compile time overrides - i.e. all the parameters are the same in the base and derived class. I'm not saying it can't be done, it's probably going to take someone far cleverer than I to sort it.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900