Connecting Tech Pros Worldwide Forums | Help | Site Map

Curiously Recurring Template Problem

Martin MacRobert
Guest
 
Posts: n/a
#1: Jul 22 '05
Hi Gang,
The following code does not compile, but I can't figure out why. The
compiler complains that the CuriouslyDerivedType (CRDerived) does not
publish the "value_type", yet in fact the class CRDerived does.

Is there any way of making the code work? I would like functions to
accept a CRBase template, and the CRBase must be able to deduce the
"value_type" of the function that it is "curiously calling".

Thanks.
Martin

------snip here 8< ------
template <typename CurioslyDerivedType>
struct CRBase
{
typedef typename CurioslyDerivedType::value_type value_type;

const CurioslyDerivedType& derived() const
{
return static_cast<const CurioslyDerivedType&>(*this);
}

value_type invoke() const
{
return derived().foo();
}
};

struct CRDerived:
public CRBase< CRDerived >
{
typedef int value_type;

int foo() const
{
return 1234;
}
};

template <typename CurioslyDerivedType>
typename CRBase<CurioslyDerivedType>::value_type
invoke(const CRBase<CurioslyDerivedType>& c)
{
return cr.invoke();
}

int main()
{
CRDerived c;
int result = invoke(c);
return 0;
}


------snip here 8< ------

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

Victor Bazarov
Guest
 
Posts: n/a
#2: Jul 22 '05

re: Curiously Recurring Template Problem


Martin MacRobert wrote:[color=blue]
> The following code does not compile, but I can't figure out why. The
> compiler complains that the CuriouslyDerivedType (CRDerived) does not
> publish the "value_type", yet in fact the class CRDerived does.
>
> Is there any way of making the code work? I would like functions to
> accept a CRBase template, and the CRBase must be able to deduce the
> "value_type" of the function that it is "curiously calling".[/color]

First of all, it has to figure out what 'value_type' is. In your
code it is generally impossible. You should think of making that
'value_type' thing in CRBase a separate template argument. Then
you break that cycle when the definition of 'value_type' in CRBase
depends on the definition of 'value_type' in the derived class,
which hasn't been defined yet.
[color=blue]
>
> Thanks.
> Martin
>
> ------snip here 8< ------
> template <typename CurioslyDerivedType>
> struct CRBase
> {
> typedef typename CurioslyDerivedType::value_type value_type;
>
> const CurioslyDerivedType& derived() const
> {
> return static_cast<const CurioslyDerivedType&>(*this);
> }
>
> value_type invoke() const
> {
> return derived().foo();
> }
> };
>
> struct CRDerived:
> public CRBase< CRDerived >[/color]

At this point it tries to instantiate CRBase with CRDerived as the
template argument. CRBase attempts to find 'value_type' member of
its template argument. But CRDerived (the actual argument) hasn't
been defined yet (at this point). So, no 'value_type' member in it
exists (the compiler hasn't got to that line yet, two lines down).

That's why it doesn't compile.
[color=blue]
> {
> typedef int value_type;
>
> int foo() const
> {
> return 1234;
> }
> };
>
> template <typename CurioslyDerivedType>
> typename CRBase<CurioslyDerivedType>::value_type
> invoke(const CRBase<CurioslyDerivedType>& c)
> {
> return cr.invoke();
> }
>
> int main()
> {
> CRDerived c;
> int result = invoke(c);
> return 0;
> }[/color]

try

template<typename CuriouslyDerivedType, typename T> class CRBase
{
typedef T value_type;
....
};

struct CRDerived : public CRBase<CRDerived, int>
{
typedef int value_type; // if you need it inside, otherwise better
// to inherit it from CRBase so that 'int'
// is only mentioned once: in the angle
// brackets of the CRBase specialisation
...
};

Victor
Daniel Krügler (ne Spangenberg)
Guest
 
Posts: n/a
#3: Jul 22 '05

re: Curiously Recurring Template Problem


Hello Martin MacRobert,

Martin MacRobert schrieb:
[color=blue]
>Hi Gang,
>The following code does not compile, but I can't figure out why. The
>compiler complains that the CuriouslyDerivedType (CRDerived) does not
>publish the "value_type", yet in fact the class CRDerived does.
>
>Is there any way of making the code work? I would like functions to
>accept a CRBase template, and the CRBase must be able to deduce the
>"value_type" of the function that it is "curiously calling".
>
>Thanks.
>Martin
>
>------snip here 8< ------
>template <typename CurioslyDerivedType>
>struct CRBase
>{
> typedef typename CurioslyDerivedType::value_type value_type;
>
> const CurioslyDerivedType& derived() const
> {
> return static_cast<const CurioslyDerivedType&>(*this);
> }
>
> value_type invoke() const
> {
> return derived().foo();
> }
>};
>
>struct CRDerived:
> public CRBase< CRDerived >
>{
> typedef int value_type;
>
> int foo() const
> {
> return 1234;
> }
>};
>
>template <typename CurioslyDerivedType>
>typename CRBase<CurioslyDerivedType>::value_type
>invoke(const CRBase<CurioslyDerivedType>& c)
>{
> return cr.invoke();
>}
>
>int main()
>{
> CRDerived c;
> int result = invoke(c);
> return 0;
>}
>
>
>------snip here 8< ------
>[/color]

The compiler is right, because CRDerived is still undefined at the point
of the definition of
the class CRBase. If you want to take advantage of CRTP you are limited
to uses of the
derived class which don't need its actual definition, e.g. inside member
functions. One
possibility is to move the static assertion into a member function of
CRBase, which **must**
be defined:

template <typename CurioslyDerivedType>
struct CRBase
{
~CRBase() {
typedef typename CurioslyDerivedType::value_type value_type;
}

..
};

Regrettably this does not help you in case of your member function:

value_type invoke() const;

The only workaround for this seems to be a further template parameter:

template <typename CurioslyDerivedType, typename ValueTypeT>
struct CRBase
{
typedef ValueTypeT value_type;

~CRBase() {
// This test ensures, that
// - CurioslyDerivedType provides a value_type name
// - that CurioslyDerivedType::value_type is identical to ValueTypeT
BOOST_STATIC_ASSERT(boost::is_same<typename CurioslyDerivedType::value_type, value_type>::value);
}
};

struct CRDerived:
public CRBase< CRDerived, int >
{
typedef int value_type;

int foo() const
{
return 1234;
}
};


Greetings from Bremen,

Daniel Krügler



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Marco Manfredini
Guest
 
Posts: n/a
#4: Jul 22 '05

re: Curiously Recurring Template Problem


Martin MacRobert wrote:
[color=blue]
> Hi Gang,
> The following code does not compile, but I can't figure out why. The
> compiler complains that the CuriouslyDerivedType (CRDerived) does not
> publish the "value_type", yet in fact the class CRDerived does.[/color]

That's because CRDerived isn't fully defined yet, when the compiler
instantiates the CRBase<> template or as a rule of thumb: He hasn seen
seen all of the body yet.

A workaround could be a helper class:

// default definition of value_type_of<T>
template<class T> struct value_type_of
{
typedef typename T::value_type type;
};

// definition of CRBase<T>...

// specialization for CRDerived, a kind of forward-declare if you want..
struct CRDerived;
template<> struct value_type_of<CRDerived>{ typedef int type; };

struct CRDerived:
public CRBase< CRDerived >
{
typedef value_type_of<CRDerived> value_type;
...
};

Marco


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
kwikius
Guest
 
Posts: n/a
#5: Jul 22 '05

re: Curiously Recurring Template Problem


mjmacrobert@yahoo.com (Martin MacRobert) wrote in message news:<273f1027.0407260138.32dbec28@posting.google. com>...[color=blue]
> Hi Gang,
> The following code does not compile, but I can't figure out why. The
> compiler complains that the CuriouslyDerivedType (CRDerived) does not
> publish the "value_type", yet in fact the class CRDerived does.
>
> Is there any way of making the code work? I would like functions to
> accept a CRBase template, and the CRBase must be able to deduce the
> "value_type" of the function that it is "curiously calling".
>
> Thanks.
> Martin
>[/color]
#include<iostream>
template< typename X>
struct traits;

template <typename CurioslyDerivedType>
struct CRBase
{
typedef typename traits<CurioslyDerivedType>::value_type value_type;
const CurioslyDerivedType& derived() const
{
return static_cast<const CurioslyDerivedType&>(*this);
}

value_type invoke() const
{
return derived().foo();
}
};

struct CRDerived;
template<>
struct traits<CRDerived>{ typedef int value_type;};
struct CRDerived : public CRBase< CRDerived >
{
typedef traits<CRDerived>::value_type value_type;

value_type foo() const
{
return 1234;
}
};

template <typename CurioslyDerivedType>
typename traits<CurioslyDerivedType>::value_type
invoke(const CRBase<CurioslyDerivedType>& c)
{
return c.invoke();
}

int main()
{
CRDerived c;
int result = invoke(c);
std::cout << result <<'\n';
return 0;
}

regards
Andy Little

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