views:

907

answers:

5

Hi everybody,

Bumped into another templates problem:

The problem: I want to partially specialize a container-class (foo) for the case that the objects are pointers, and i want to specialize only the delete-method. Should look like this:

The lib code

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};

template <typename T>
class foo <T *>
{
public:
    void deleteSome (T* o) { printf ("deleting that PTR to an object..."); }
};

The user code

foo<myclass> myclasses;
foo<myclass*> myptrs;

myptrs.addSome (new myclass());

This results into the compiler telling me that myptrs doesnt have a method called addSome. Why ?

Thanx.


Solution

based on tony's answer here the fully compilable stuff

lib

template <typename T>
class foobase
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};


template <typename T>
class foo : public foobase<T>
{ };

template <typename T>
class foo<T *> : public foobase<T *>
{
public:
    void deleteSome (T* o) { printf ("deleting that ptr to an object..."); }
};

user

foo<int>    fi;
foo<int*>   fpi;

int   i = 13;

fi.addSome (12);      
fpi.addSome (&i);

fpi.deleteSome (12);     // compiler-error: doesnt work
fi.deleteSome (&i);   // compiler-error: doesnt work
fi.deleteSome (12);   // foobase::deleteSome called
fpi.deleteSome (&i);     // foo<T*>::deleteSome called
+6  A: 

Second solution (correct one)

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); } 
    void deleteSome(T o) { deleteSomeHelper<T>()(o); }
protected:
    template<typename TX> 
    struct deleteSomeHelper { void operator()(TX& o) { printf ("deleting that object..."); } };
    template<typename TX> 
    struct deleteSomeHelper<TX*> { void operator()(TX*& o) { printf ("deleting that PTR to an object..."); } };
};

This solution is valid according to Core Issue #727.


First (incorrect) solution: (kept this as comments refer to it)

You cannot specialize only part of class. In your case the best way is to overload function deleteSome as follows:

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
    void deleteSome (T* o) { printf ("deleting that object..."); }
};
Kirill V. Lyadvinsky
Well, thats a good suggestion, but it also enables the following case to compile without error:- foo<int> fi; int i; fi.deleteSome ( Where it should ofcourse only be able to "deleteSome" of type int
Roman Pfneudl
No. No. If `T` is `myclass*` then `T *` `is myclass**`
Alexey Malistov
@Kirill. That will not work. Suppose we use foo<myclass*>. Then foo has 3 function: `addSome (myclass * o)`, `deleteSome (myclass * o)` and `deleteSome (myclass ** o)`. Last function never will be called.
Alexey Malistov
Added second solution.
Kirill V. Lyadvinsky
Johannes Schaub - litb
@litb, Now I see what you are talking about. Fixed.
Kirill V. Lyadvinsky
@Kirill. Mark your first solution as wrong.
Alexey Malistov
+1 for second solution. Looks good. See below for an idea using boost::remove_pointer, avoiding the extra function.
Marcus Lindblom
+1  A: 

You can use inheritance to get this to work :

template <typename T>
class foobase
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};

template <typename T>
class foo : public foobase<T>
{ };

template <typename T>
class foo <T *> : public foobase<T>
{
public:
    void deleteSome (T* o) { printf ("deleting that PTR to an object..."); }
};
Tony
Thanx tony, i guess im going with your solution.
Roman Pfneudl
This solution is not scalable. Suppose you want add new function doSomething(T o) and specialize it for the case that the objects are std::list<T>.
Alexey Malistov
Also this solution is wrong. Assume we use foo<int*>. No function of the `class foobase` can call `deleteSome` because `deleteSome (T)` will be called rather than `deleteSome (T*)`.
Alexey Malistov
Um, shouldn't that be `class foo<T*> : public foobase<T*>` (note the second `*`)?
sbi
Kirill's answer misses one of the uses for specialization, namely to specialize an existing class for your own new type (without having the ability to modify the existing class). Tony's solution is the most general.
foxcub
-1 there is no reason `deleteSome` should be in the base class without being virtual, pick up either option.
Matthieu M.
+1  A: 

Create base class for single function deleteSome

template<class T>
class base {
public:
  void deleteSome (T o) { printf ("deleting that object..."); }
}

Make partial specialization

template<class T>
class base<T*> {
public:
  void deleteSome (T * o) { printf ("deleting that PTR to an object..."); }
}

Use your base class

template <typename T>
class foo : public base<T> {
 public:    
   void addSome    (T o) { printf ("adding that object..."); 
}
Alexey Malistov
+5  A: 

Another solution. Use the auxiliary function deleteSomeHelp.

template <typename T>
class foo {
 public:    
   void addSome    (T o) { printf ("adding that object..."); 
   template<class R>
   void deleteSomeHelp (R   o) { printf ("deleting that object..."); }};
   template<class R>
   void deleteSomeHelp (R * o) { printf ("deleting that PTR to an object..."); }};
   void deleteSome (T o) { deleteSomeHelp(o); }
}
Alexey Malistov
crazy stuff :), but im facing the same problem here, so that a foo<int> CAN call a deleteSome with a pointer given.
Roman Pfneudl
+1 Looks like the one i would use right away. It's much simplier and gets the job done.
Johannes Schaub - litb
+2  A: 

I haven't seen this solution yet, using boost's enable_if, is_same and remove_pointer to get two functions in a class, without any inheritance or other cruft.

See below for a version using only remove_pointer.

#include <boost\utility\enable_if.hpp>
#include <boost\type_traits\is_same.hpp>
#include <boost\type_traits\remove_pointer.hpp>

template <typename T>
class foo
{
public:
    typedef typename boost::remove_pointer<T>::type T_noptr;

    void addSome    (T o) { printf ("adding that object..."); }

    template<typename U>
    void deleteSome (U o, typename boost::enable_if<boost::is_same<T_noptr, U>>::type* dummy = 0) { 
        printf ("deleting that object..."); 
    }
    template<typename U>
    void deleteSome (U* o, typename boost::enable_if<boost::is_same<T_noptr, U>>::type* dummy = 0) { 
        printf ("deleting that PTR to that object..."); 
    }
};

A simplified version is:

#include <cstdio>
#include <boost\type_traits\remove_pointer.hpp>

template <typename T>
class foo
{
public:
    typedef typename boost::remove_pointer<T>::type T_value;

    void addSome    (T o) { printf ("adding that object..."); }

    void deleteSome (T_value& o) { // need ref to avoid auto-conv of double->int
        printf ("deleting that object..."); 
    }

    void deleteSome (T_value* o) { 
        printf ("deleting that PTR to that object..."); 
    }
};

And it works on MSVC 9: (commented out lines that give errors, as they are incorrect, but good to have for testing)

void main()
{
   foo<int> x;
   foo<int*> y;

   int a;
   float b;

   x.deleteSome(a);
   x.deleteSome(&a);
   //x.deleteSome(b); // doesn't compile, as it shouldn't
   //x.deleteSome(&b);
   y.deleteSome(a);
   y.deleteSome(&a);
   //y.deleteSome(b);
   //y.deleteSome(&b);
}
Marcus Lindblom
That's really interesting
pythonic metaphor