Static polymorphism -- what's wrong?

Discussion in 'C++' started by shablool, Oct 13, 2005.

  1. shablool

    shablool Guest

    Hi all,
    Could someone please explain what is wrong with this code (the compiler
    complains about "'int_type' is not a member of 'B'"):


    struct B;

    template <typename DerivedT>
    struct A
    {
    typedef typename DerivedT::int_type int_type;

    int_type get(void) const { return 0; }
    };


    struct B : public A<B>
    {
    typedef int int_type;
    };

    int main()
    {
    B b;
    return b.get();
    }
    shablool, Oct 13, 2005
    #1
    1. Advertising

  2. shablool

    Greg Guest

    shablool wrote:
    > Hi all,
    > Could someone please explain what is wrong with this code (the compiler
    > complains about "'int_type' is not a member of 'B'"):
    >
    >
    > struct B;
    >
    > template <typename DerivedT>
    > struct A
    > {
    > typedef typename DerivedT::int_type int_type;
    >
    > int_type get(void) const { return 0; }
    > };
    >
    >
    > struct B : public A<B>
    > {
    > typedef int int_type;
    > };
    >
    > int main()
    > {
    > B b;
    > return b.get();
    > }


    It seems unlikely that struct A is able to typedef a typedef from one
    of its derived classes.

    Reversing the typedefs to avoid the circular dependency does fix the
    problem:

    struct B;

    template <typename DerivedT>
    struct A
    {
    typedef int int_type;

    int_type get() const { return 0; }
    };

    struct B : public A<B>
    {
    using A<B>::int_type;
    };

    int main()
    {
    B b;
    return b.get();
    }

    Greg
    Greg, Oct 13, 2005
    #2
    1. Advertising

  3. shablool

    mlimber Guest

    shablool wrote:
    > Hi all,
    > Could someone please explain what is wrong with this code (the compiler
    > complains about "'int_type' is not a member of 'B'"):
    >
    >
    > struct B;
    >
    > template <typename DerivedT>
    > struct A
    > {
    > typedef typename DerivedT::int_type int_type;
    >
    > int_type get(void) const { return 0; }
    > };
    >
    >
    > struct B : public A<B>
    > {
    > typedef int int_type;
    > };
    >
    > int main()
    > {
    > B b;
    > return b.get();
    > }


    The problem is that B is not defined yet, but you're trying to use it
    in A<B>. You can either do what Greg said, or you can break your B
    class apart:

    struct BAttributes { typedef int int_type; };

    template <class T>
    struct A
    {
    typedef typename T::int_type int_type;
    int_type get() const { return 0; }
    };

    struct B : public A<BAttributes>, public BAttributes {};

    int main()
    {
    B b;
    return b.get();
    }

    Note that the multiple inheritance is technically unnecessary here
    since A<> duplicates all its types. You would need it only if there
    were additional functions, data, or types that A<> did not "copy". BTW,
    an easier way to transfer the contents of BAttributes to A<> would be
    to treat it like a policy class:

    struct BAttributes { typedef int int_type; };

    template <class Attributes>
    struct A : public Attributes
    {
    int_type get() const { return 0; }
    };

    Cheers! --M
    mlimber, Oct 13, 2005
    #3
  4. shablool

    Puppet_Sock Guest

    Greg wrote:
    [snip context]
    > struct B;
    >
    > template <typename DerivedT>
    > struct A
    > {
    > typedef int int_type;
    >
    > int_type get() const { return 0; }
    > };
    >
    > struct B : public A<B>
    > {
    > using A<B>::int_type;
    > };
    >
    > int main()
    > {
    > B b;
    > return b.get();
    > }


    Ok, that freaks me out. I'd never have thought that you
    could derive a class from a template of the same class.
    I guess it's because, in this case, A<> only refers to
    DerivedT in ways that only need the forward declaration.
    If you were to, for example, include a data member in A<>
    of type DerivedT, then it would bust. (And the compiler
    I use agrees, for what that's worth.)
    Socks
    Puppet_Sock, Oct 13, 2005
    #4
  5. shablool

    mlimber Guest

    Puppet_Sock wrote:
    > Greg wrote:
    > [snip context]
    > > struct B;
    > >
    > > template <typename DerivedT>
    > > struct A
    > > {
    > > typedef int int_type;
    > >
    > > int_type get() const { return 0; }
    > > };
    > >
    > > struct B : public A<B>
    > > {
    > > using A<B>::int_type;
    > > };
    > >
    > > int main()
    > > {
    > > B b;
    > > return b.get();
    > > }

    >
    > Ok, that freaks me out. I'd never have thought that you
    > could derive a class from a template of the same class.
    > I guess it's because, in this case, A<> only refers to
    > DerivedT in ways that only need the forward declaration.
    > If you were to, for example, include a data member in A<>
    > of type DerivedT, then it would bust. (And the compiler
    > I use agrees, for what that's worth.)
    > Socks


    I use that technique for automatic registration with a factory object.
    Assuming Factory<> and Singleton<> classes as found in _Modern C++
    Design_'s Loki, I have this in a header file:

    // Helper classes for object creation by a factory with a default
    // ctor. Concrete classes must implement a static function GetID().
    template<class AbstractClass, class ConcreteClass>
    class DefaultCreator
    {
    public:
    static std::auto_ptr<AbstractClass> Create()
    {
    // Verify that the given types are actually super and sub classes
    STATIC_CHECK( SUPERSUBCLASS( AbstractClass, ConcreteClass ),
    types_are_not_super_and_subclass );

    // While the factory will also check at run-time to see if a class
    // is registered, we assert on m_registered here to cause a link-
    // time failure if we forget to register a concrete command class.
    assert( m_registered );

    return std::auto_ptr<AbstractClass>( new ConcreteClass );
    }
    private:
    typedef Singleton< Factory<AbstractClass> > theFactory;
    static const bool m_registered;
    };


    // Automatically register all classes that inherit from
    // DefaultCreator<>. (Note that these static members appear in header
    // file because they are themselves templates. The compiler will
    // ensure that there is only one instance of each globally.)

    template<class AbstractClass, class ConcreteClass>
    const bool DefaultCreator<AbstractClass,ConcreteClass>::m_registered =
    DefaultCreator<AbstractClass,ConcreteClass>::theFactory::Instance()
    .Register( ConcreteClass::GetID(), ConcreteClass::Create );


    And I use it like so:

    class Base {};

    class Derived
    : public Base
    , public DefaultCreator<Base, Derived>
    {
    public:
    static int GetAssignedID() { return 42; }
    // ...
    };

    Then the stuff _Modern C++ Design_ does to register classes with the
    factory is automatic for any class inheriting from DefaultCreator<>.

    Cheers! --M
    mlimber, Oct 13, 2005
    #5
  6. shablool

    mlimber Guest

    mlimber wrote:
    > class Derived
    > : public Base
    > , public DefaultCreator<Base, Derived>
    > {
    > public:
    > static int GetAssignedID() { return 42; }
    > // ...
    > };

    [snip]

    Oops. Copy-paste-and-simplify error. That function should be:

    static int GetID() { return 42; }

    Cheers! --M
    mlimber, Oct 13, 2005
    #6
    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. rwf_20
    Replies:
    15
    Views:
    851
    mlimber
    Nov 17, 2005
  2. Krivenok Dmitry
    Replies:
    13
    Views:
    1,430
    Axter
    Jun 1, 2006
  3. Replies:
    8
    Views:
    589
  4. crjjrc
    Replies:
    8
    Views:
    425
    James Kanze
    Apr 5, 2007
  5. newbie
    Replies:
    6
    Views:
    400
Loading...

Share This Page