Curiously Recurring Template Problem

M

Martin MacRobert

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< ------
 
V

Victor Bazarov

Martin said:
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".

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.
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 >

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.
{
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;
}

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
 
?

=?ISO-8859-1?Q?=22Daniel_Kr=FCgler_=28ne_Spangenbe

Hello Martin MacRobert,

Martin said:
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< ------

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
 
M

Marco Manfredini

Martin said:
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.

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
 
K

kwikius

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
#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
 

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

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top