12

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
Roman Pfneudl
  • 707
  • 1
  • 8
  • 21
  • 1
    Wrong solution. Assume that we use foo. Suppose some function of the `class foobase` calls deleteSome. What function will be called? Right! deleteSome(T) of the `class foobase` rather than deleteSome(T *). – Alexey Malistov Nov 18 '09 at 19:02
  • Similar question: http://stackoverflow.com/questions/1501357/template-specialization-of-particular-members – Johannes Schaub - litb Nov 18 '09 at 19:21
  • @Malistov: I believe the point is that foobase shouldn't be used. It's just there to make the hack possible. – int3 Nov 18 '09 at 19:27
  • @splicer, but it *will* be used implicitly if you call a function other than `deleteSome`. By that point, if the called function calls `deleteSome`, it will never reach the function in the derived classs. – Johannes Schaub - litb Nov 18 '09 at 20:37
  • @JohannesSchaub-litb What if the base class specifies the function to be virtual (and isn't instantiatede)? I've seen this problem / design pattern creep up before: does it have a name? – user Apr 22 '13 at 19:14

5 Answers5

11

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..."); }
};
Macke
  • 24,812
  • 7
  • 82
  • 118
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • Well, thats a good suggestion, but it also enables the following case to compile without error: - foo fi; int i; fi.deleteSome (&i); Where it should ofcourse only be able to "deleteSome" of type int – Roman Pfneudl Nov 18 '09 at 18:05
  • @Kirill. That will not work. Suppose we use foo. Then foo has 3 function: `addSome (myclass * o)`, `deleteSome (myclass * o)` and `deleteSome (myclass ** o)`. Last function never will be called. – Alexey Malistov Nov 18 '09 at 18:26
  • i'll go shopping now. But i'll leave you a note: You cannot put the explicit specialization for the member template into the class body. You also cannot put it outside, as this won't work: `template template<> void foo::deleteSomeHelper(T &o) { }` (as there may not be any template parameter clauses before a `template<>`). – Johannes Schaub - litb Nov 18 '09 at 18:32
  • @litb, Now I see what you are talking about. Fixed. – Kirill V. Lyadvinsky Nov 18 '09 at 19:08
  • +1 for second solution. Looks good. See below for an idea using boost::remove_pointer, avoiding the extra function. – Macke Nov 18 '09 at 19:31
10

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
  • 26,407
  • 13
  • 68
  • 88
3

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);
}
Macke
  • 24,812
  • 7
  • 82
  • 118
2

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
  • 26,407
  • 13
  • 68
  • 88
1

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..."); }
};
  • Thanx tony, i guess im going with your solution. – Roman Pfneudl Nov 18 '09 at 18:28
  • 3
    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. – Alexey Malistov Nov 18 '09 at 18:38
  • 1
    Also this solution is wrong. Assume we use foo. No function of the `class foobase` can call `deleteSome` because `deleteSome (T)` will be called rather than `deleteSome (T*)`. – Alexey Malistov Nov 18 '09 at 19:29
  • 1
    Um, shouldn't that be `class foo : public foobase` (note the second `*`)? – sbi Nov 19 '09 at 09:34
  • 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 Nov 19 '09 at 14:52
  • -1 there is no reason `deleteSome` should be in the base class without being virtual, pick up either option. – Matthieu M. Nov 19 '09 at 15:07