Reputation: 32
I'm having a problem with function definitions in template classes.
I have a template class Array
that has a member function IndexOf
:
// Defined in header
template <typename T>
class Array {
// Other stuff
int IndexOf(const T value) const {
// Code of function
};
// Other stuff
};
When I create an instance of the array with a class pointer, The function ignores the const T
and just uses T
. So when I call the function with a const T
, there is a compiler error: "No instance of overloaded function":
#include "Array.h"
class Object{
// Object stuff
};
int main(){
// Demonstration code
Array<Object*> objects = Array<Object*>(); // Array of Object*
Object* obj = new Object(); // An Object*
const Object* cobj = new Object(); // A const Object*
// This works fine
objects.IndexOf(obj); // No problems
// This has compile error
objects.IndexOf(cobj); // No idea why
delete obj;
delete cobj;
return 0;
}
The code seems to forget that the function's parameter is const T
. The function parameter should be const Object*
, but it treats the parameter like its just Object*
.
Any idea why this happens?
The error does not occur if I am not using a pointer as the type.
The code inside the function would not cause problems.
Before it is said, no, I will not use std::vector
or others of the like to have a dynamically resizing array. That takes all the fun out of it.
Upvotes: 0
Views: 149
Reputation: 41331
You can declare Array<Object*>::IndexOf()
as template:
template <typename U>
int IndexOf(const U& value) const {
// Code of function
}
and inside you'll compare value
to the contents of the array and that comparison will cast the contents from Object*
to const Object*
which is of course allowed.
Also with this approach you can pass to that function anything that T
can be converted from/to.
Upvotes: 0
Reputation: 12384
In reality your declaration of the function can be seen as:
IndexOf(const (Object *) val)
which basically means that the Object *
should point to the const object as here:
Object *const cobj = new Object();
Now you will have correct behavior. However if you are really interested in const pointers, then you need to modify your declaration as:
Array<const Object *> objects;
Upvotes: 2
Reputation: 29072
const T value
where T
is Object*
is Object* const value
. The const
in const T value
applies to value
and does not modify T
. const T value
is a value
of type T
that can't change, which for pointers means can't be assigned a new address. It's clear that the following function could not be called with a const Object *
.
int IndexOf(Object * const value) const // const pointer to non-const Object
To add const
ness to the pointed-to type, you can use type-traits to get the pointed-to type, add const to it and then produce a new pointer type from it. For example :
#include <type_traits>
template<class T>
using add_const_to_ptr =
std::add_pointer_t<
std::add_const_t<
std::remove_pointer_t<T>>>;
The usage of which would look like :
template<class T>
struct container {
void push(add_const_to_ptr<T> ptr) {
(void)ptr;
}
};
struct bar {};
int main()
{
const bar cbar;
bar mbar;
container<bar*> my_container;
my_container.push(&mbar);
my_container.push(&cbar);
}
Though this assumes container
will only be used with pointers, in which case it may be better to make T
refer to the type of object pointed to, rather than a pointer type. If T
may or may not be a pointer type, the type trait can be improved by checking if T
is actually a pointer with std::conditional
:
#include <type_traits>
template<class T>
using add_const_to_ptr =
std::conditional_t<std::is_pointer<T>::value,
std::add_pointer_t<
std::add_const_t<
std::remove_pointer_t<T>>>,
T>;
Upvotes: 0