Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CRTP and typedef

279 views
Skip to first unread message

pfultz2

unread,
Feb 1, 2010, 4:04:40 PM2/1/10
to
I seem to have a problem with CRTP and typedefs, here is a sample
code:

template<class B>
class Sequence
{
public:
typedef typename B::ElementType ElementType;
}

template<class T>
class Container
{
public:
typedef T ElementType;
}

Containter<char> test;

but the compiler says that Container<char>::ElementType doesnt exist?
is it possible to refer to subtypes using CRTP? or perhaps i have a
error some other place in my code?
is there a way to forward declare the type and the subtypes like this:

template<class T>
class Container;

//Then forward declare subtype here
template<class T>
class Containter<T>::ElementType;

Or maybe there is another way, thanks

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

pfultz2

unread,
Feb 2, 2010, 3:14:47 PM2/2/10
to
On Feb 1, 4:04 pm, pfultz2 <pful...@yahoo.com> wrote:
> I seem to have a problem with CRTP and typedefs, here is a sample
> code:
>
> template<class B>
> class Sequence
> {
> public:
> typedef typename B::ElementType ElementType;
>
> }
>
> template<class T>
> class Container
> {
> public:
> typedef T ElementType;
>
> }
>
> Containter<char> test;
>
> but the compiler says that Container<char>::ElementType doesnt exist?
> is it possible to refer to subtypes using CRTP? or perhaps i have a
> error some other place in my code?
> is there a way to forward declare the type and the subtypes like this:
>
> template<class T>
> class Container;
>
> //Then forward declare subtype here
> template<class T>
> class Containter<T>::ElementType;
>
> Or maybe there is another way, thanks

{ edits: quoted banner removed. please remove irrelevant material before
posting. -mod }

I dont know if i made it clear in the code, let me try to rewrite it:

template<class B>
class Sequence
{
public:

typedef typename B::Iterator Iterator;


typedef typename B::ElementType ElementType;

}

template<class T>
class Container : public Sequence<Container<T>>
{
public:
typedef T ElementType;
class Iterator
{
};

}

Now i get an error saying that B::Iterator and B::ElementType doesnt
exist. Does anyone know of some workarounds? I need to use the those
types in various places, I could infer the type from the method I call
using typeof or decltyp. I need to use it in the return types in
several places like this:
ElementType First()
{
return this->Derived().GetIterator().First();
}

ReverseIterator<Iterator> Reverse()
{
return ReverseIterator<Iterator>(this->Derived().GetIterator());
}

Where the Derived() method casts the this pointer to the derived
class.
Is this even possible? Or is there a better way to do this? The only
other way i can think of is to use macros, but this is not very nice,
and is harder to mantain.

Chris Uzdavinis

unread,
Feb 2, 2010, 3:13:41 PM2/2/10
to
On Feb 1, 3:04 pm, pfultz2 <pful...@yahoo.com> wrote:
> I seem to have a problem with CRTP and typedefs, here is a sample
> code:

Poor sample, as it is invalid code (missing semicolons, has typos,
etc),
it doesn't show any CRTP at all, and therefore does not show what you
are
describing.

Corrected: (Educated guess)

template<class B>
class Sequence
{
public:
typedef typename B::ElementType ElementType;
};

template<class T>


class Container : public Sequence<Container<T> >
{
public:
typedef T ElementType;

};

Container<char> test;


The problem, regardless, is that at the point where Container<T>
inherits from Sequence,
it is an incomplete type and is thus not able to be interrogated for
nested types.

The easiest solution is to pass up another template parameter for the
element type:

template<class Derived, typename ElementT>
class Sequence
{
public:
typedef ElementT ElementType;
};

template<class T>
class Container : public Sequence<Container<T>, T>
{
// ...
};


Chris

Johannes Schaub (litb)

unread,
Feb 2, 2010, 3:13:52 PM2/2/10
to
pfultz2 wrote:

> I seem to have a problem with CRTP and typedefs, here is a sample
> code:
>
> template<class B>
> class Sequence
> {
> public:
> typedef typename B::ElementType ElementType;
> }
>
> template<class T>
> class Container
> {
> public:
> typedef T ElementType;
> }
>
> Containter<char> test;
>
> but the compiler says that Container<char>::ElementType doesnt exist?
> is it possible to refer to subtypes using CRTP? or perhaps i have a
> error some other place in my code?
> is there a way to forward declare the type and the subtypes like this:
>
> template<class T>
> class Container;
>
> //Then forward declare subtype here
> template<class T>
> class Containter<T>::ElementType;
>
> Or maybe there is another way, thanks
>

I'm assuming you actually have an inheritance relationship, and forgot to
write that in your testcase. Well, at the point "Sequence<Container>" is
instantiated (which happens immediately when the base class name is seen),
Container::ElementType has not yet been declared, and so the instantiation
of the typedef declaration in Sequence will fail to find it.

You can put that as a template argument. What about

template<typename T, typename Typedefs>
class Sequence {
typedef typename Typedefs::ElementType ElementType;
// ...
};

Jeff Schwab

unread,
Feb 2, 2010, 3:24:35 PM2/2/10
to
pfultz2 wrote:
> I seem to have a problem with CRTP and typedefs, here is a sample
> code:
>
> template<class B>
> class Sequence
> {
> public:
> typedef typename B::ElementType ElementType;
> }
>
> template<class T>
> class Container
> {
> public:
> typedef T ElementType;
> }
>
> Containter<char> test;

There is no CRTP in that code, you're missing two semicolons, and there
is no such template as Containter.

> but the compiler says that Container<char>::ElementType doesnt exist?
> is it possible to refer to subtypes using CRTP? or perhaps i have a
> error some other place in my code?

It's impossible to tell you what the error is without seeing the actual
code, or at least the actual diagnostic message from the compiler. The
following is valid C++ that does what you seem to want:

namespace {

template<class B>
struct sequence
{
typedef typename B::element_type element_type;
};

template<class T>
struct container
{
typedef T element_type;
};

typedef container<char> container_t;
typedef sequence<container_t> sequence_t;

container_t const chars = container_t( );
sequence_t const containers = sequence_t( );
}

#include <cassert>

int main() {
assert(sizeof(sequence_t::element_type) == 1);

pfultz2

unread,
Feb 2, 2010, 6:11:27 PM2/2/10
to
On Feb 2, 3:13 pm, "Johannes Schaub (litb)" <schaub-johan...@web.de>
wrote:

{ edits: quoted banner removed. please remove extraneous material before
posting. -mod }

Well that would work for the first type but if its an inner class,
what work around would i use?
for example:
template<class B, class Iterator>
class Sequence
{
public:
typename B::ElementType Calculate()
{
return 1;
}

typename B::Iterator Get()
{
return typename B::Iterator();
}

};

template<class T>


class Container : public Sequence<Container<T> >
{
public:
typedef T ElementType;

class Iterator
{

};
};

/*
*
*/
int main(int argc, char** argv)
{
Container<int> test;
printf("%i", test.Calculate());
return (EXIT_SUCCESS);
}

I could acces ElementType, but how do i access Iterator?

Ulrich Eckhardt

unread,
Feb 3, 2010, 9:46:57 AM2/3/10
to
pfultz2 wrote:
> template<class B>
> class Sequence
> {
> public:
> typedef typename B::Iterator Iterator;
> typedef typename B::ElementType ElementType;
> }

Again, this declaration is missing a semicolon, so this can not be the code
you tried to compile...

> template<class T>
> class Container : public Sequence<Container<T>>
> {
> public:
> typedef T ElementType;
> class Iterator
> {
> };
> }

...as does this one.

> Now i get an error saying that B::Iterator and B::ElementType doesnt
> exist.

This doesn't help. Provide the *real* code, not some substitute that more or
less resembles that code. I'm left to guessing what you really did now...


> I need to use it in the return types in several places like this:
> ElementType First()
> {
> return this->Derived().GetIterator().First();
> }

Where is that? I guess it's a memberfunction of Sequence<>, right? If so, I
believe you can just write

typename B::ElementType First() { [...] }

The simple reason is that while these are parsed to some extent, they are
not actually instantiated as early. That is what allows an inline member
function to refer to a variable declared later in the class definition.

> Is this even possible? Or is there a better way to do this?

Several alternatives exist:
* Provide a template parameter for the ElementType.
* (Maybe) the workaround above.
* Use a common traits-class that defines the ElementType both for
Sequence<> and Container<>.
* Define the ElementType in the baseclass instead of the derived class.

> The only other way i can think of is to use macros, but this is not
> very nice, and is harder to mantain.

How would that way look like?

Uli

--
Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932

pfultz2

unread,
Feb 3, 2010, 5:42:01 PM2/3/10
to

>
> This doesn't help. Provide the *real* code, not some substitute that more or
> less resembles that code. I'm left to guessing what you really did now...

Well i have reposted the code, trying to use an inner class also, here
it is again:


template<class B>
class Sequence
{
public:

typename B::ElementType Calculate()
{
return 1;
}

typename B::Iterator Get()
{
return typename B::Iterator();
}

};

template<class T>


class Container : public Sequence<Container<T> >
{
public:
typedef T ElementType;

class Iterator
{

};

};

/*
*
*/
int main(int argc, char** argv)
{
Container<int> test;
printf("%i", test.Calculate());
return (EXIT_SUCCESS);

}

> Several alternatives exist:


> * Provide a template parameter for the ElementType.
> * (Maybe) the workaround above.
> * Use a common traits-class that defines the ElementType both for
> Sequence<> and Container<>.
> * Define the ElementType in the baseclass instead of the derived class.

This would work for ElementType but not for Iterator, since it is a
subclass. But maybe i can use some kind of common traits class for
that also, and place the iterator in there. Only this makes the design
a little more messy, perhaps not.


>
> > The only other way i can think of is to use macros, but this is not
> > very nice, and is harder to mantain.
>
> How would that way look like?

Well i would define all the methods from sequence in a macro like this

#define SEQUENCE(B) \
B::ElementType Calculate()\
{\
return 1;\
}\
\
B::Iterator Get()\
{\
return typename B::Iterator();\
}\

and then in the container class i would add the macro like this:

template<class T>
class Container
{

public:
typedef T ElementType;

class Iterator
{

};

SEQUENCE(Container<T>)

};

This will work, i just dont like using macros like this, first because
it invades the global namespace, and secondly it can be messy to write
the code for Sequence, because everything has to be accessed at the
global namespace, so if some classes are used three or more levels
deep in a namespace, it makes it much more difficult to read. Thirdly,
you can't override the routines in the derived class either, which it
would be nice if they could be overriden in some cases for more
effecient routines in some cases. I thought CRTP would be great option
for this, and i dont know if i can overcome its limitiations in this
case.

--

0 new messages