12

Suppose I have something like:

template <class T>
void do_something(T t){
  pass_it_somewhere(t);
  t->do_something();
}

Now it would be useful that T is allowed to be a pointer- or a non-pointer type. Function do_something(...) can basically handle pointers and non-pointers, except for the t->do_something(). For pointers, I would need a -> and for non-pointers, I would need a . to access members.

Is there a way to make T accept pointers and non-pointers?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Michael
  • 7,407
  • 8
  • 41
  • 84
  • While this question has some interesting answers, I eventually also found a great solution here: https://stackoverflow.com/questions/14466620/c-template-specialization-calling-methods-on-types-that-could-be-pointers-or/14466705 – Bernhard Dec 04 '18 at 10:28

3 Answers3

13

You could create a dereference mechanism as below:

template<typename T>
std::enable_if_t<std::is_pointer<T>::value, std::remove_pointer_t<T>&> dereference(T& t) { 
  return *t; 
}

template<typename T>
std::enable_if_t<!std::is_pointer<T>::value, T&> dereference(T& t) {
  return t;
}

and use it in your function as:

template <class T>
void do_something(T t){
  pass_it_somewhere(dereference(t));
  dereference(t).do_something();
}

Live Demo

This way you'll have to do only with concrete versions of T.

101010
  • 41,839
  • 11
  • 94
  • 168
  • 1
    I think that's the best answer since it results in very readable and concise code. Thanks :-) – Michael Apr 28 '16 at 15:52
8

Soultion 1

Use template specialization:

template <class T>
void do_something(T t){
  pass_it_somewhere(t);
  t.do_something();
}

template <class T>
void do_something(T* t){
  pass_it_somewhere(t);    
  t->do_something();
}

Solution 2

Add a user-defined pointer operator in class T:

class A
{
public:
    void do_something() const {}        
    const A* operator->() const { return this; }
};

template <class T>
void do_something(T t){
  pass_it_somewhere(t);      
  t->do_something();
}
Mohamad Elghawi
  • 2,071
  • 10
  • 14
3

Yet another solution: tag dispatching.

namespace detail {
    struct tag_value {};
    struct tag_ptr {};

    template <bool T>  struct dispatch       { using type = tag_value; };
    template <>        struct dispatch<true> { using type = tag_ptr;   };

    template <class T>
    void do_call(T v, tag_value)
    {
      v.call();
    }

    template <class T>
    void do_call(T ptr, tag_ptr)
    {
       ptr->call();
    }
}

Then your function becomes:

template <class T>
void do_something(T unknown)
{
   do_call(unknown, 
                typename detail::dispatch<std::is_pointer<T>::value>::type{} );

   // found by ADL

}

Live Example.

edmz
  • 8,220
  • 2
  • 26
  • 45