8

Considering the following code:

class MyClass
{
    ...
};

template <typename Object>
class List
{
public:

    void insert(const Object & x)
    {
        // call when Object is MyClass
    }

    void insert(const Object & x)
    {
        // call when Object is MyClass*
    }
}

int main()
{
    MyClass a;

    List<MyClass> lst;
    List<MyClass*> plst;

    lst.insert(a);
    plst.insert(new Myclass);

    return 0;
}

How to tell the compiler call different methods based on if the template is a class or a pointer?

How to fix the code above?

barej
  • 1,330
  • 3
  • 25
  • 56
  • 2
    Please post an [MCVE](http://stackoverflow.com/help/mcve). – Baum mit Augen May 31 '15 at 10:08
  • 1
    The better approach would be to re-write your code in such a way that it doesn't care whether it's dealing with a class or a pointer. The standard `list` and `vector` classes don't care, why does your `List`? What is it doing that depends on the template argument being a class type? –  May 31 '15 at 10:32

4 Answers4

11

You can use a combination of std::is_pointer and std::enable_if:

#include <type_traits>
#include <iostream>

class MyClass
{
};

template <typename Object>
class List
{
public:

    template<class T=Object>
    void insert(T t, typename std::enable_if<std::is_pointer<T>::value >::type* = 0) 
    {
        std::cout << "insert pointer" << std::endl;
    }

    template<class T=Object>
    void insert(T t, typename std::enable_if<!std::is_pointer<T>::value >::type* = 0) 
    {
        std::cout << "insert non-pointer" << std::endl;
    }
};

int main()
{
    MyClass a;

    List<MyClass> lst;
    List<MyClass*> plst;

    lst.insert(a);
    plst.insert(new MyClass());

    return 0;
}

Live example: https://ideone.com/CK8Zdo

This will allow you to insert both pointers and non-pointers into a pointer or non-pointer list. If you want to restrict that, you can use this:

#include <type_traits>
#include <iostream>
class MyClass
{
};

template <typename Object>
class List
{
public:

    template<class T=Object>
    void insert(T t, typename std::enable_if<std::is_same<T,Object>::value&&std::is_pointer<T>::value >::type* = 0) 
    {
        std::cout << "insert pointer" << std::endl;
    }

    template<class T=Object>
    void insert(const T& t, typename std::enable_if<std::is_same<T,Object>::value&&!std::is_pointer<T>::value >::type* = 0) 
    {
        std::cout << "insert non-pointer" << std::endl;
    }
};

int main()
{
    MyClass a;

    List<MyClass> lst;
    List<MyClass*> plst;

    lst.insert(a);
    // plst.insert(a); // compiler error

    // lst.insert(new MyClass()); // compiler error
    plst.insert(new MyClass());


    return 0;
}

Live example: https://ideone.com/3DtBfr

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • I'm getting a compile error when trying to use this method. `error: failed requirement 'std::is_pointer::value'; 'enable_if' cannot be used to disable this declaration` – einstein Jul 14 '19 at 09:32
  • @einstein which example? which compiler? – m.s. Jul 15 '19 at 05:49
  • I just forgot to reassign the template argument for the class in the method as in your example. But after doing that, it works great! – einstein Jul 17 '19 at 14:59
0

I'm aware that my answer is not exactly about what you are asking, but maybe it could help.

I believe your intention is to have List class with one insert method (not two of them) and behaviour of this method should depend on your template parameter. For this you could write a specialization of your class for pointers. Then basic template would be used for non pointer types and specialization would be used for pointer types.

Your code would look like this:

template <typename Object>
class List
{
public:

    void insert(const Object & x)
    {
        // call when Object is MyClass
    }
};

template <typename Object>
class List<Object *>
{
public:

    void insert(Object * x)
    {
        // call when Object is MyClass*
    }
};
robal
  • 368
  • 2
  • 8
0
void insert(const Object & x)
{
    M_insert(x, dispatcher<std::is_pointer<Object>::value> );
}

Inside List use a dispatcher

template <bool B> class dispatcher {};
using ObjectPtr   = dispatcher<true>;
using ObjectValue = dispatcher<false>;

then dispatch to M_insert:

void M_insert(const Object &p, ObjectPtr) { // Object is a pointer }
void M_insert(const Object &p, ObjectValue) { // Object is not a pointer }

Live example here. But, I'd encourage you to determine whether you really need that and possibly fix your design accordingly.

edmz
  • 8,220
  • 2
  • 26
  • 45
0

This does the trick:

template <typename Object>
class List
{
public:

    template<class C = Object>
    void insert(const C & x)
    {
        // call when Object is MyClass
        std::cout << "1" << "\n" ;
    }

    template<class P = Object*>
       void insert(P* p)
    {
        // call when Object is MyClass*
        std::cout << "2" << "\n" ;
    }
} ;

Here is a working example.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • But this will allow to insert non-`Object` data types which I don't believe it's allowed. – edmz May 31 '15 at 11:31