2

Given the following declaration:

template<class T>
class A {
  void run(T val) {
    val.member ...
  }
}

This code works fine if no pointers are used:

A<Type> a;
Type t;
a.run(t);

But using a pointer results in an error:

A<Type*> a;
Type* t = new Type();
a.run(t);

error: request for member ‘member’ which is of non-class type ‘T*’

Obviously in this case the member must be accessed via ->. What's the best way to handle this?

I found a solution on SO: Determine if Type is a pointer in a template function

template<typename T>
struct is_pointer { static const bool value = false; };

template<typename T>
struct is_pointer<T*> { static const bool value = true; };

...

if (is_pointer<T>::value) val->member
else val.member

But this is very verbose. Any better ideas?

Community
  • 1
  • 1
Alex
  • 34,581
  • 26
  • 91
  • 135

2 Answers2

3

You could use a simple pair of overloaded function templates:

template<typename T>
T& access(T& t) { return t; }

template<typename T>
T& access(T* t) { return *t; }

And then use them this way:

access(val).member = 42;

For instance:

template<typename T>
struct A
{
    void do_it(T& val)
    {
        access(val).member = 42;
    }
};

struct Type
{
    int member = 0;
};

#include <iostream>

int main()
{
    A<Type> a;
    Type t;
    a.do_it(t);
    std::cout << t.member << std::endl;

    A<Type*> a2;
    Type* t2 = new Type(); // OK, I don't like this, but just to show
                           // it does what you want it to do...
    a2.do_it(t2);
    std::cout << t2->member;

    delete t2;             // ...but then, don't forget to clean up!
}

Here is a live example.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • That's great! Very simple, exactly what I was looking for. Thank you sir :) – Alex Mar 20 '13 at 17:00
  • By the way, you don't like dynamic allocation here because no smart pointers are used, right? – Alex Mar 20 '13 at 17:03
  • @Alex: Right, and also because there is no real need for a pointer. A pointer is used when you need reference semantics, so that side-effects on one object will be visible in multiple places referencing the same object. Here, we only have one function working with (actually, owning) that object, so there's no need for either a pointer, not even a smart one. In fact, I should have probable written `Type t2; a2.do_it(&t2);` :) – Andy Prowl Mar 20 '13 at 17:06
2

The best idea is probably to specialize your class for pointer types.

template<class T>
class A{ ...}; 

template<>
class A<T*> { //implement for pointers
};

If you feel that this is too verbose, you can use overload a get_ref function:

template<class T> T& get_ref(T & r) {return r;}
template<class T> T& get_ref(T* r) {return *r;}

template<class T>
class A {
   void do(T val) {
     get_ref(val).member ...
  }
}
sbabbi
  • 11,070
  • 2
  • 29
  • 57