Curiously Recurring Template Problem

Discussion in 'C++' started by Martin MacRobert, Jul 26, 2004.

  1. 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! ]
     
    Martin MacRobert, Jul 26, 2004
    #1
    1. Advertising

  2. Martin MacRobert wrote:
    > 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
     
    Victor Bazarov, Jul 26, 2004
    #2
    1. Advertising

  3. Hello Martin MacRobert,

    Martin MacRobert schrieb:

    >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



    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    =?ISO-8859-1?Q?=22Daniel_Kr=FCgler_=28ne_Spangenbe, Jul 26, 2004
    #3
  4. Martin MacRobert wrote:

    > 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


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Marco Manfredini, Jul 26, 2004
    #4
  5. Martin MacRobert

    kwikius Guest

    (Martin MacRobert) wrote in message news:<>...
    > 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

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    kwikius, Jul 26, 2004
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. velthuijsen

    recurring template problem

    velthuijsen, Apr 6, 2004, in forum: C++
    Replies:
    2
    Views:
    399
    velthuijsen
    Apr 7, 2004
  2. Denis Remezov
    Replies:
    7
    Views:
    598
    tom_usenet
    Apr 7, 2004
  3. iuweriur
    Replies:
    15
    Views:
    936
    Jonathan Turkanis
    Jul 12, 2004
  4. AndrewD
    Replies:
    4
    Views:
    574
    AndrewD
    Jan 26, 2007
  5. Replies:
    4
    Views:
    678
    Zeppe
    Jul 11, 2007
Loading...

Share This Page