template specialization

Discussion in 'C++' started by Konstantin, Sep 13, 2008.

  1. Konstantin

    Konstantin Guest

    I have two possible implementations of my class:

    template <typename T>
    class MyContainer
    {
    public:
    typedef typename std::set<T>::const_iterator const_iterator;
    void add( T id ) { data.insert(id); }
    void remove( T id ) { data.erase(id); }
    private:
    std::set<T> data;
    };


    template <typename T>
    class MyContainer
    {
    public:
    typedef typename std::list<T>::const_iterator const_iterator;
    void add( T id ) { data.push_back(id); }
    void remove( T id ) { data.remove(id); }
    private:
    std::list<T> data;
    };


    which suggests that another parameter in the template could be added.

    How to specialize the template correctly so that I could, for example, switch the underlying container:


    typedef MyContainer<InstanceID, set>::const_iterator mycontainer_iterator;
    MyContainer<InstanceID, set> mycontainer;

    or

    typedef MyContainer<InstanceID, list>::const_iterator mycontainer_iterator;
    MyContainer<InstanceID, list> mycontainer;


    --
    Konstantin
    Konstantin, Sep 13, 2008
    #1
    1. Advertising

  2. On Sep 13, 5:51 pm, Konstantin <> wrote:
    > I have two possible implementations of my class:
    >
    > template <typename T>
    > class MyContainer
    > {
    > public:
    >     typedef typename std::set<T>::const_iterator const_iterator;
    >     void add( T id ) { data.insert(id); }
    >     void remove( T id ) { data.erase(id); }
    > private:
    >     std::set<T> data;
    >
    > };
    >
    > template <typename T>
    > class MyContainer
    > {
    > public:
    >     typedef typename std::list<T>::const_iterator const_iterator;
    >     void add( T id ) { data.push_back(id); }
    >     void remove( T id ) { data.remove(id); }
    > private:
    >     std::list<T> data;
    >
    > };
    >
    > which suggests that another parameter in the template could be added.
    >
    > How to specialize the template correctly so that I could, for example, switch the underlying container:


    It's not clear what your problem is. If you want to write container
    adaptors, try looking at some examples of those in the standard
    library for example, std::priority_queue<>. You would need an extra
    template parameter for the container to choose as you rightly
    observerd. But I am not sure what specialization you are asking of?
    Are you saying your template should be instantiable only on std::set
    and std::list?

    >
    > typedef MyContainer<InstanceID, set>::const_iterator mycontainer_iterator;
    > MyContainer<InstanceID, set> mycontainer;
    >
    > or
    >
    > typedef MyContainer<InstanceID, list>::const_iterator mycontainer_iterator;
    > MyContainer<InstanceID, list> mycontainer;
    Abhishek Padmanabh, Sep 13, 2008
    #2
    1. Advertising

  3. Konstantin

    Konstantin Guest

    Abhishek Padmanabh wrote:
    > On Sep 13, 5:51 pm, Konstantin <> wrote:
    >> I have two possible implementations of my class:
    >>
    >> template <typename T>
    >> class MyContainer
    >> {
    >> public:
    >> typedef typename std::set<T>::const_iterator const_iterator;
    >> void add( T id ) { data.insert(id); }
    >> void remove( T id ) { data.erase(id); }
    >> private:
    >> std::set<T> data;
    >>
    >> };
    >>
    >> template <typename T>
    >> class MyContainer
    >> {
    >> public:
    >> typedef typename std::list<T>::const_iterator const_iterator;
    >> void add( T id ) { data.push_back(id); }
    >> void remove( T id ) { data.remove(id); }
    >> private:
    >> std::list<T> data;
    >>
    >> };
    >>
    >> which suggests that another parameter in the template could be added.
    >>
    >> How to specialize the template correctly so that I could, for example, switch the underlying container:

    >
    > It's not clear what your problem is. If you want to write container
    > adaptors, try looking at some examples of those in the standard
    > library for example, std::priority_queue<>. You would need an extra
    > template parameter for the container to choose as you rightly
    > observerd. But I am not sure what specialization you are asking of?
    > Are you saying your template should be instantiable only on std::set
    > and std::list?



    Yes.



    >> typedef MyContainer<InstanceID, set>::const_iterator mycontainer_iterator;
    >> MyContainer<InstanceID, set> mycontainer;
    >>
    >> or
    >>
    >> typedef MyContainer<InstanceID, list>::const_iterator mycontainer_iterator;
    >> MyContainer<InstanceID, list> mycontainer;

    >
    Konstantin, Sep 13, 2008
    #3
  4. Konstantin

    mqrk Guest

    The simplest (and probably the best) solution is like this:

    template < class, template < class > class > class MyContainer;

    template <typename T>
    class MyContainer< T, std::set >
    {
    public:
    typedef typename std::set<T>::const_iterator const_iterator;
    void add( T id ) { data.insert(id); }
    void remove( T id ) { data.erase(id); }
    private:
    std::set<T> data;

    };

    template <typename T>
    class MyContainer< T, std::list >
    {
    public:
    typedef typename std::list<T>::const_iterator const_iterator;
    void add( T id ) { data.push_back(id); }
    void remove( T id ) { data.remove(id); }
    private:
    std::list<T> data;

    };

    This solution doesn't scale particularly well. If you're doing this
    with more than just two container types, you might just want to have
    one definition of MyContainer, and handle the rest with policies. It
    might look something like this: (this is just a rough outline)

    template < class Container > class DefaultAccessPolicy;

    template < typename T, template <typename> class Container, template <
    class > class AccessPolicy = DefaultAccessPolicy >
    class MyContainer
    {
    public:
    typedef typename Container::const_iterator const_iterator;
    void add( T id ) { AccessPolicy< Container<T> >::add( &data,
    id ); }
    void remove( T id ) { AccessPolicy< Container<T> >::remove( &data,
    id ); }
    private:
    Container<T> data;
    };

    template < typename T >
    class DefaultAccessPolicy< std::list<T> >
    {
    public:
    static void add( std::list<T>* l, T t ) { l->push_back( t ); }
    static void remove( std::list<T>* l, T t ) { l->remove( t ); }
    };

    //Ditto for std::set and so on

    Now you only have one definition of the main class, which may cut down
    on maintenance. For example, when you decide that you don't only need
    const_iterator, but want a plain iterator as well, you only have to
    modify one class. Also, if you later decide you want to change how a
    particular instance of a container-adapter operates (maybe a list that
    uses push_front, or only removes the first match), you just have to
    write a new policy.

    Regards,
    Mark McKenna
    mqrk, Sep 13, 2008
    #4
  5. Konstantin

    Konstantin Guest

    Yes, this is what I was looking for. Thanks.


    mqrk wrote:
    > The simplest (and probably the best) solution is like this:
    >
    > template < class, template < class > class > class MyContainer;
    >
    > template <typename T>
    > class MyContainer< T, std::set >
    > {
    > public:
    > typedef typename std::set<T>::const_iterator const_iterator;
    > void add( T id ) { data.insert(id); }
    > void remove( T id ) { data.erase(id); }
    > private:
    > std::set<T> data;
    >
    > };
    >
    > template <typename T>
    > class MyContainer< T, std::list >
    > {
    > public:
    > typedef typename std::list<T>::const_iterator const_iterator;
    > void add( T id ) { data.push_back(id); }
    > void remove( T id ) { data.remove(id); }
    > private:
    > std::list<T> data;
    >
    > };
    >
    > This solution doesn't scale particularly well. If you're doing this
    > with more than just two container types, you might just want to have
    > one definition of MyContainer, and handle the rest with policies. It
    > might look something like this: (this is just a rough outline)
    >
    > template < class Container > class DefaultAccessPolicy;
    >
    > template < typename T, template <typename> class Container, template <
    > class > class AccessPolicy = DefaultAccessPolicy >
    > class MyContainer
    > {
    > public:
    > typedef typename Container::const_iterator const_iterator;
    > void add( T id ) { AccessPolicy< Container<T> >::add( &data,
    > id ); }
    > void remove( T id ) { AccessPolicy< Container<T> >::remove( &data,
    > id ); }
    > private:
    > Container<T> data;
    > };
    >
    > template < typename T >
    > class DefaultAccessPolicy< std::list<T> >
    > {
    > public:
    > static void add( std::list<T>* l, T t ) { l->push_back( t ); }
    > static void remove( std::list<T>* l, T t ) { l->remove( t ); }
    > };
    >
    > //Ditto for std::set and so on
    >
    > Now you only have one definition of the main class, which may cut down
    > on maintenance. For example, when you decide that you don't only need
    > const_iterator, but want a plain iterator as well, you only have to
    > modify one class. Also, if you later decide you want to change how a
    > particular instance of a container-adapter operates (maybe a list that
    > uses push_front, or only removes the first match), you just have to
    > write a new policy.
    >
    > Regards,
    > Mark McKenna
    Konstantin, Sep 13, 2008
    #5
  6. Konstantin

    Konstantin Guest

    mqrk wrote:
    > The simplest (and probably the best) solution is like this:
    >



    The problem still remains:


    template <typename T> struct Container< T, std::set >
    {
    std::set<T> data;
    };

    template <typename T> struct Container< T, std::list >
    {
    std::list<T> data;
    };


    These are specializations, and one needs to write a primary template.
    However, std::set template has three parameters: set<Key, Compare,
    Alloc>, and std::list has only two: list<Type, Alloc>

    Thus, if the primary template is

    template < typename, template < typename, typename, typename > class >
    class Container;

    then the second specialization yields an error ("error C3201: the
    template parameter list for class template 'std::list' does not match
    the template parameter list for template parameter ...")

    and if the primary template is

    template < typename T, template<typename, typename> class >
    class Container;

    then the "set" template yields an error, and "list" works fine.




    The same problem is in the solution with policies: the line
    template <typename> class Container
    in
    template < typename T,
    template <typename> class Container,
    template <class > class AccessPolicy = DefaultAccessPolicy >
    class MyContainer

    will match neither std::set nor std::list for the same reason; adding
    more arguments, e.g. template <typename,typename> class Container breaks
    generality.


    Is there a mechanism to reconcile these two? (E.g. something like
    var-arg template arguments...)


    Konstantin.
    Konstantin, Sep 15, 2008
    #6
  7. Konstantin

    mqrk Guest

    On Sep 14, 8:40 pm, Konstantin <> wrote:
    > ... if the primary template is
    > template < typename T, template<typename, typename> class >
    > class Container;
    >
    > then the "set" template yields an error, and "list" works fine.


    Sorry about that. It works with gcc 4.1.3 as long as there are fewer
    template parameters in the template template parameter then there are
    in the template template argument. I didn't know this wasn't
    standard.

    > Is there a mechanism to reconcile these two? (E.g. something like
    > var-arg template arguments...)


    Not that I can think of. There will probably be variadic templates in
    the next standard though (for all the good that does you now).
    mqrk, Sep 15, 2008
    #7
  8. Konstantin

    Kai-Uwe Bux Guest

    mqrk wrote:

    > On Sep 14, 8:40 pm, Konstantin <> wrote:
    >> ... if the primary template is
    >> template < typename T, template<typename, typename> class >
    >> class Container;
    >>
    >> then the "set" template yields an error, and "list" works fine.

    >
    > Sorry about that. It works with gcc 4.1.3 as long as there are fewer
    > template parameters in the template template parameter then there are
    > in the template template argument. I didn't know this wasn't
    > standard.
    >
    >> Is there a mechanism to reconcile these two? (E.g. something like
    >> var-arg template arguments...)

    >
    > Not that I can think of. There will probably be variadic templates in
    > the next standard though (for all the good that does you now).


    The ad-hoc way would be to define

    template < typename T, typename A, typename C >
    struct Set : public std::set< T, A, C > {
    // some forwarding constructors (templated)
    }

    template < typename T, typename A, typename C >
    struct List : public std::list< T, A > {
    // some forwarding constructors (templated)
    }

    and then use these instead of std::set and std::list.


    Best

    Kai-Uwe Bux
    Kai-Uwe Bux, Sep 15, 2008
    #8
  9. Konstantin

    Konstantin Guest

    Oh, great! Thank you all.

    Konstantin.



    Kai-Uwe Bux wrote:
    > mqrk wrote:
    >
    >> On Sep 14, 8:40 pm, Konstantin <> wrote:
    >>> ... if the primary template is
    >>> template < typename T, template<typename, typename> class >
    >>> class Container;
    >>>
    >>> then the "set" template yields an error, and "list" works fine.

    >> Sorry about that. It works with gcc 4.1.3 as long as there are fewer
    >> template parameters in the template template parameter then there are
    >> in the template template argument. I didn't know this wasn't
    >> standard.
    >>
    >>> Is there a mechanism to reconcile these two? (E.g. something like
    >>> var-arg template arguments...)

    >> Not that I can think of. There will probably be variadic templates in
    >> the next standard though (for all the good that does you now).

    >
    > The ad-hoc way would be to define
    >
    > template < typename T, typename A, typename C >
    > struct Set : public std::set< T, A, C > {
    > // some forwarding constructors (templated)
    > }
    >
    > template < typename T, typename A, typename C >
    > struct List : public std::list< T, A > {
    > // some forwarding constructors (templated)
    > }
    >
    > and then use these instead of std::set and std::list.
    >
    >
    > Best
    >
    > Kai-Uwe Bux
    Konstantin, Sep 15, 2008
    #9
    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. David B. Held
    Replies:
    2
    Views:
    459
    Rob Williscroft
    Oct 26, 2003
  2. Dave
    Replies:
    4
    Views:
    7,647
    pdixtl
    Jun 4, 2010
  3. pit3k
    Replies:
    8
    Views:
    360
    Victor Bazarov
    Feb 9, 2005
  4. case2005
    Replies:
    3
    Views:
    1,791
    Nicolas Pavlidis
    Feb 13, 2005
  5. Joseph Turian
    Replies:
    2
    Views:
    455
Loading...

Share This Page