Reputation: 343
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
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
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
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