Explicit specialization of member template when type is anothertemplate type.

Discussion in 'C++' started by jason.cipriani@gmail.com, Dec 5, 2008.

  1. Guest

    Here is a program, it looks weird but it models what I'm trying to do.
    In this program:

    - "Object" is just some example type.
    - "Container" holds a single item of some type, but provides access
    access to a copy of that object only via a nested class
    Container::ContainedItem.

    === BEGIN ===

    // An example object with a member function.
    class Object {
    public:
    void function () const { }
    };

    template <typename T> class Container {
    private:
    T data_; // Container's instance of the data.
    public:
    // This just wraps an item.
    class ContainedItem {
    private:
    T item_; // Some copy of the data.
    public:
    explicit ContainedItem (const T &item) : item_(item) { }
    const T * getItem () const; // This gets the copy.
    };
    // Get via ContainedItem.
    ContainedItem getData () const { return ContainedItem(data_); }
    // Set value directly.
    void setData (const T &data) { data_ = data; }
    };

    // Generic template implementation.
    template <typename T>
    const T * Container<T>::ContainedItem::getItem () const {
    return &item_;
    }

    === END ===


    Now, my problem is very specific: When T is an arbitrary type I want
    Container<T>::ContainedItem::getItem() to return a const T *, just
    like it already does. However, when T specifically is a
    boost::shared_ptr<X>, I want Container<T>::ContainedItem::getItem() to
    return a const X *, not a const T *. For example:


    === BEGIN ===

    #include <boost/shared_ptr.hpp>
    using boost::shared_ptr;

    int main () {
    Container<Object> x;
    Container<shared_ptr<Object> > y;
    x.setData(Object()); // i want this
    y.setData(shared_ptr<Object>()); // i want this
    x.getData().getItem()->function(); // i want this
    y.getData().getItem()->get()->function(); // don't want this!
    //y.getData().getItem()->function(); // i want this instead!
    }

    === END ===


    Basically, as shown above, for shared_ptr<X> template types, I don't
    want the extra "get()" in there (boost::shared_ptr<X> does not define
    a conversion to X*). Is there some way I can make an explicit
    specialization for getItem(), for type T = boost::shared_ptr<X>, and
    have it return a const X * (it would return "item_.get()" instead of
    "&item_")? I can't figure out the syntax for it, if it's possible.

    Is there some other solution instead? It's not that I don't want to
    type "get()", it's that I want Container<X> and
    Container<shared_ptr<X> > to have getData().getItem() return the same
    type for both.

    Thanks,
    Jason
    , Dec 5, 2008
    #1
    1. Advertising

  2. Guest

    On Dec 5, 12:25 am, ""
    <> wrote:
    > Here is a program, it looks weird but it models what I'm trying to do.
    > In this program:
    >
    >   - "Object" is just some example type.
    >   - "Container" holds a single item of some type, but provides access
    > access to a copy of that object only via a nested class
    > Container::ContainedItem.
    >
    > === BEGIN ===
    >
    > // An example object with a member function.
    > class Object {
    > public:
    >         void function () const { }
    >
    > };
    >
    > template <typename T> class Container {
    > private:
    >         T data_; // Container's instance of the data.
    > public:
    >         // This just wraps an item.
    >         class ContainedItem {
    >         private:
    >                 T item_; // Some copy of the data.
    >         public:
    >                 explicit ContainedItem (const T &item) : item_(item) { }
    >                 const T * getItem () const; // This gets the copy.
    >         };
    >         // Get via ContainedItem.
    >         ContainedItem getData () const { return ContainedItem(data_); }
    >         // Set value directly.
    >         void setData (const T &data) { data_ = data; }
    >
    > };
    >
    > // Generic template implementation.
    > template <typename T>
    > const T * Container<T>::ContainedItem::getItem () const {
    >         return &item_;
    >
    > }
    >
    > === END ===
    >
    > Now, my problem is very specific: When T is an arbitrary type I want
    > Container<T>::ContainedItem::getItem() to return a const T *, just
    > like it already does. However, when T specifically is a
    > boost::shared_ptr<X>, I want Container<T>::ContainedItem::getItem() to
    > return a const X *, not a const T *. For example:
    >
    > === BEGIN ===
    >
    > #include <boost/shared_ptr.hpp>
    > using boost::shared_ptr;
    >
    > int main () {
    >         Container<Object> x;
    >         Container<shared_ptr<Object> > y;
    >         x.setData(Object()); // i want this
    >         y.setData(shared_ptr<Object>()); // i want this
    >         x.getData().getItem()->function(); // i want this
    >         y.getData().getItem()->get()->function(); // don't want this!
    >         //y.getData().getItem()->function(); // i want this instead!
    >
    > }
    >
    > === END ===
    >
    > Basically, as shown above, for shared_ptr<X> template types, I don't
    > want the extra "get()" in there (boost::shared_ptr<X> does not define
    > a conversion to X*). Is there some way I can make an explicit
    > specialization for getItem(), for type T = boost::shared_ptr<X>, and
    > have it return a const X * (it would return "item_.get()" instead of
    > "&item_")? I can't figure out the syntax for it, if it's possible.
    >
    > Is there some other solution instead? It's not that I don't want to
    > type "get()", it's that I want Container<X> and
    > Container<shared_ptr<X> > to have getData().getItem() return the same
    > type for both.



    I'm sorry, I mistitled this post. Originally I had been playing with
    member templates and that's what I had on my mind when I typed the
    subject. Should probably read "template member" I guess, or something.

    Jason
    , Dec 5, 2008
    #2
    1. Advertising

  3. Triple-DES Guest

    On 5 Des, 06:25, "" <>
    wrote:

    > === BEGIN ===
    >
    > // An example object with a member function.
    > class Object {
    > public:
    >         void function () const { }
    >
    > };
    >
    > template <typename T> class Container {
    > private:
    >         T data_; // Container's instance of the data.
    > public:
    >         // This just wraps an item.
    >         class ContainedItem {
    >         private:
    >                 T item_; // Some copy of the data.
    >         public:
    >                 explicit ContainedItem (const T &item) : item_(item) { }
    >                 const T * getItem () const; // This gets the copy.
    >         };
    >         // Get via ContainedItem.
    >         ContainedItem getData () const { return ContainedItem(data_); }
    >         // Set value directly.
    >         void setData (const T &data) { data_ = data; }
    >
    > };
    >
    > // Generic template implementation.
    > template <typename T>
    > const T * Container<T>::ContainedItem::getItem () const {
    >         return &item_;
    >
    > }
    >
    > === END ===
    >
    > Now, my problem is very specific: When T is an arbitrary type I want
    > Container<T>::ContainedItem::getItem() to return a const T *, just
    > like it already does. However, when T specifically is a
    > boost::shared_ptr<X>, I want Container<T>::ContainedItem::getItem() to
    > return a const X *, not a const T *. For example:
    >
    > === BEGIN ===
    >
    > #include <boost/shared_ptr.hpp>
    > using boost::shared_ptr;
    >
    > int main () {
    >         Container<Object> x;
    >         Container<shared_ptr<Object> > y;
    >         x.setData(Object()); // i want this
    >         y.setData(shared_ptr<Object>()); // i want this
    >         x.getData().getItem()->function(); // i want this
    >         y.getData().getItem()->get()->function(); // don't want this!
    >         //y.getData().getItem()->function(); // i want this instead!
    >
    > }
    >
    > === END ===


    Note that
    template <typename T> const T * Container< shared_ptr<T>
    >::ContainedItem::

    getItem () const

    is a partial specialization of the member function getItem, and you
    cannot partially specialize member function of class templates. To
    avoid having to specialize the entire class template, you could
    perhaps do something like this:

    template<typename T> struct ContainerTraits {
    typedef const T * ReturnType;
    static ReturnType get(const T& t) { return &t; }
    };

    template<typename T> struct ContainerTraits< shared_ptr<T> > {
    typedef const T * ReturnType;
    static ReturnType get(const shared_ptr<T>& p) { return p.get(); }
    };

    // declaration of getItem:
    typename ContainerTraits<T>::ReturnType getItem () const;

    // definition of getItem:
    template <typename T> typename ContainerTraits< T >::ReturnType
    Container<T>::ContainedItem::getItem () const {
    return ContainerTraits<T>::get(item_);
    }

    int main() {
    Container<Object>().getData().getItem()->function(); // ok
    Container< shared_ptr<Object> >().getData().getItem()->function
    (); // ok
    }
    Triple-DES, Dec 5, 2008
    #3
  4. Guest

    On Dec 5, 2:28 am, Triple-DES <> wrote:
    > On 5 Des, 06:25, "" <>
    > wrote:
    >
    >
    >
    > > === BEGIN ===

    >
    > > // An example object with a member function.
    > > class Object {
    > > public:
    > >         void function () const { }

    >
    > > };

    >
    > > template <typename T> class Container {
    > > private:
    > >         T data_; // Container's instance of the data.
    > > public:
    > >         // This just wraps an item.
    > >         class ContainedItem {
    > >         private:
    > >                 T item_; // Some copy of the data.
    > >         public:
    > >                 explicit ContainedItem (const T &item) : item_(item) { }
    > >                 const T * getItem () const; // This gets the copy.
    > >         };
    > >         // Get via ContainedItem.
    > >         ContainedItem getData () const { return ContainedItem(data_); }
    > >         // Set value directly.
    > >         void setData (const T &data) { data_ = data; }

    >
    > > };

    >
    > > // Generic template implementation.
    > > template <typename T>
    > > const T * Container<T>::ContainedItem::getItem () const {
    > >         return &item_;

    >
    > > }

    >
    > > === END ===

    >
    > > Now, my problem is very specific: When T is an arbitrary type I want
    > > Container<T>::ContainedItem::getItem() to return a const T *, just
    > > like it already does. However, when T specifically is a
    > > boost::shared_ptr<X>, I want Container<T>::ContainedItem::getItem() to
    > > return a const X *, not a const T *. For example:

    >
    > > === BEGIN ===

    >
    > > #include <boost/shared_ptr.hpp>
    > > using boost::shared_ptr;

    >
    > > int main () {
    > >         Container<Object> x;
    > >         Container<shared_ptr<Object> > y;
    > >         x.setData(Object()); // i want this
    > >         y.setData(shared_ptr<Object>()); // i want this
    > >         x.getData().getItem()->function(); // i want this
    > >         y.getData().getItem()->get()->function(); // don't want this!
    > >         //y.getData().getItem()->function(); // i want this instead!

    >
    > > }

    >
    > > === END ===

    >
    > Note that
    > template <typename T> const T * Container< shared_ptr<T>>::ContainedItem::
    >
    > getItem () const
    >
    > is a partial specialization of the member function getItem, and you
    > cannot partially specialize member function of class templates. To
    > avoid having to specialize the entire class template, you could
    > perhaps do something like this:
    >
    > template<typename T> struct ContainerTraits {
    >   typedef const T * ReturnType;
    >    static ReturnType get(const T& t) { return &t; }
    >
    > };
    >
    > template<typename T> struct ContainerTraits< shared_ptr<T> > {
    >   typedef const T * ReturnType;
    >   static ReturnType get(const shared_ptr<T>& p)  { return p.get(); }
    >
    > };
    >
    > // declaration of getItem:
    > typename ContainerTraits<T>::ReturnType getItem () const;
    >
    > // definition of getItem:
    > template <typename T> typename ContainerTraits< T >::ReturnType
    > Container<T>::ContainedItem::getItem () const {
    >   return ContainerTraits<T>::get(item_);
    >
    > }
    >
    > int main() {
    >   Container<Object>().getData().getItem()->function(); // ok
    >   Container< shared_ptr<Object> >().getData().getItem()->function
    > (); // ok
    >
    > }



    Oh, I think I get it, that's pretty neat. So... the purpose of
    ContainerTraits is to take anything you'd need to specialize out of a
    member function that can't be partially specialized, and move it into
    a class that can be? And also, I guess it can simplify more complex
    templates by letting you use, say, ContainerTraits<T>::ReturnType
    everywhere instead of having to make specializations all over the
    place.

    Thanks,
    Jason
    , Dec 5, 2008
    #4
  5. Triple-DES Guest

    On 5 Des, 09:16, "" <>
    wrote:
    > On Dec 5, 2:28 am, Triple-DES <> wrote:


    > Oh, I think I get it, that's pretty neat. So... the purpose of
    > ContainerTraits is to take anything you'd need to specialize out of a
    > member function that can't be partially specialized, and move it into
    > a class that can be? And also, I guess it can simplify more complex
    > templates by letting you use, say, ContainerTraits<T>::ReturnType
    > everywhere instead of having to make specializations all over the
    > place.
    >


    Yes, in fact I made the trait class just for the return type. Then I
    realized that the logic for getting the T* had to be specialized as
    well. I suppose the class template could be turned into a full-fledged
    function object, since it is no longer a pure "traits" class
    (according to some definitions of the term).

    I think it is a fairly common approach to delegate to a function
    object when you want something like partial specialization for a
    function template (or a member function of a template). At least I
    have done this myself occasionally.
    Triple-DES, Dec 5, 2008
    #5
  6. Guest

    On Dec 5, 6:09 am, Triple-DES <> wrote:
    > On 5 Des, 09:16, "" <>
    > wrote:
    >
    > > On Dec 5, 2:28 am, Triple-DES <> wrote:
    > > Oh, I think I get it, that's pretty neat. So... the purpose of
    > > ContainerTraits is to take anything you'd need to specialize out of a
    > > member function that can't be partially specialized, and move it into
    > > a class that can be? And also, I guess it can simplify more complex
    > > templates by letting you use, say, ContainerTraits<T>::ReturnType
    > > everywhere instead of having to make specializations all over the
    > > place.

    >
    > Yes, in fact I made the trait class just for the return type. Then I
    > realized that the logic for getting the T* had to be specialized as
    > well. I suppose the class template could be turned into a full-fledged
    > function object, since it is no longer a pure "traits" class
    > (according to some definitions of the term).
    >
    > I think it is a fairly common approach to delegate to a function
    > object when you want something like partial specialization for a
    > function template (or a member function of a template). At least I
    > have done this myself occasionally.


    Cool, thanks for the help!

    Jason
    , Dec 5, 2008
    #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.

Share This Page