bbudescu
bbudescu

Reputation: 343

Passing a const pointer argument to a member function of a template class

Consider the following class:

template<typename T> class A{
public:
    virtual void foo(const T& v){ m_v = v + 1; }    
    T& bar(){ return m_v;}
    T m_v;
    // ... other member functions/variables ...
};

How can it be modified such that the following code works (without, if possible having to to a const_cast on p):

int main(){
    A<int*> a;
    const int* p = nullptr;
    a.foo(p);
    a.bar();

    A<int>().foo(10);
}

The problem is that the signature of the foo method, when A is templated by int* becomes, as far as I can tell, foo(int* const&). What we would like to have would be something like foo(const int* const&).

Until now, I considered adding a specialized implementation of the foo member function for A templated by pointer types outside the class declaration (as one would do for specializing A<int>::foo), but the compiler is not able to resolve the function definition prototype to the declared method.

template<typename T>
void A<T*>::foo(const T* const& ){}

gcc complains that it's an invalid use of incomplete type ‘class A<T*>’

I've also considered adding a partial specialization for the whole class for pointer types which defines an extra member overloading foo that takes in a const T*, but I couldn't figure out how to reuse the rest of the code in the base template (i.e. without duplicating the declarations and definitions for all the other functions, e.g. bar). Is there a way to reference the base template class from the pointer partial specialization, either for inheritance or for having a member to which to forward calls? (This post provides a solution by adding an extra dummy template argument. Is there a way to circumvent this?).

Finally, I've also thought about using enable_if<is_pointer<T>::value, void>::type foo(const PointedToType*); to add an extra overload to the foo method when the template parameter is a pointer, but how can one get PointedToType from T (when it is known that T is a pointer type)?

Upvotes: 2

Views: 985

Answers (3)

Barry
Barry

Reputation: 303800

Seems to me that you just need a trait for what the argument type of foo should be:

template <typename T>
struct arg_type { using type = T const&; };

template <typename T>
struct arg_type<T*> { using type = T const*; };

template <typename T>
using arg_type_t = typename arg_type<T>::type;

To be used thusly:

virtual void foo(arg_type_t<T> arg ) {}

Upvotes: 3

plong
plong

Reputation: 1753

You could just define a with a const template argument, i.e., A<const int*> a;. That would work.

template<typename T> class A{
public:
    void foo(const T &v){ m = v; }
    void bar(T &v){ v = m; }
    T m;
    // ... other member functions/variables ...
};

int main(){
    A<const int*> a;
    const int* p = nullptr;
    a.foo(p);
    a.bar(p);
}

Upvotes: 0

5gon12eder
5gon12eder

Reputation: 25459

I'm not sure if this is what you want but you could use a combination of std::is_pointer, std::remove_pointer and std::conditional to construct the argument type you want. I don't know how you would write the function body to do useful things with that argument, however.

#include <type_traits>

template <typename T>
struct A
{
  template <
    typename U = T,
    typename ArgT = std::conditional_t<
      std::is_pointer<U>::value,
      const std::remove_pointer_t<U> *,
      U>
  >
  void
  foo(const ArgT&)
  {
    // ...
  }
};

I'm using the C++14 type meta-functions here. If you cannot use C++14, replace std::fancy_t< … > with typename std::fancy< … >::type.

The following is now valid:

int
main()
{
  {
    A<int *> a {};
    const int * p {};
    a.foo(p);
  }
  {
    A<int> a {};
    const int i {};
    a.foo(i);
  }
}

Upvotes: 1

Related Questions