Consider the following template class:
template <typename T> class Function {
public:
virtual float eval( const T &x, const T &y ) = 0;
};
Since the 'eval' function should not modify the value of the two inputs 'x' and 'y', I put them as 'const'. Then I create the following class derived from Function
class Foo1 : public Function <float*> {
public:
Foo1() : Function <float*> () {}
virtual float eval( const float* &x, const float* &y ) { ... }
};
When I compile with g++, I get the following warning:
hidden overloaded virtual function 'Function<float *>::eval' declared here: type mismatch at 1st parameter
('float *const &' vs 'const float *&')
virtual float eval( const T &x, const T &y ) = 0;
And I cannot instantiate the class Foo1. The compiler says that the function 'eval' is not implemented. To make the compiler happy, the derived class must be as follows:
class Foo2 : public Function <float*> {
public:
Foo2() : Function <float*> () {}
virtual float eval( float* const &x, float* const &y ) { ... }
};
Foo2::eval function uses two parameters of type 'float * const' instead of 'const float *'. In other words, Foo2::eval can modify the content of the arrays 'x' and 'y'. This is not what I want.
I have tried to change the template class 'Function' as follows:
virtual float eval( T const &x, T const &y ) = 0;
But the class Foo1 still does not work, the class Foo2 works as in previous case.
Thank you.
The behaviour you observe is 100% correct as far as the standard is concerned. It's the classic example of "constant pointer" versus "pointer to constant."
Your primary template declares it takes a "reference to T
through which it cannot modify the T
object being referred to" (the syntax is const T &
, equivalent to T const &
).
Then, you instantiate the template with a type float*
, i.e. "pointer to float
." From this follows that the function parameter type after template parameter substitution is indeed "reference to float *
through which it cannot modify the float *
begin referred to." There's no way to smuggle "cannot modify the float
to which the float *
being referred to points" into there directly.
I see two options. One, if this kind of use is the only use of T
in Function
, simply use const float *
as the template argument, since that's the T
you really want:
class Foo1 : public Function <const float*> {
public:
Foo1() : Function <const float*> () {}
virtual float eval( const float* const &x, const float* const &y ) { ... }
// notice two `const` keywords above: one for the pointed object, one for the reference
};
If that is not an option for you (i.e. if you need float *
somewhere inside Function
and const float*
elsewhere), you'll have to use some type traits and modify the parameters of eval
. Something like this:
template <class T>
struct unmodifiable {
typedef const T &type;
};
template <class T>
struct unmodifiable<T*> {
typedef const T* const &type; // if you want to keep the reference
// or just:
// typedef const T *type; // if you don't want to bother with the reference for pointer types
};
template <typename T> class Function {
public:
virtual float eval( typename unmodifiable<T>::type x, typename unmodifiable<T>::type y ) = 0;
};
class Foo1 : public Function <float*> {
public:
Foo1() : Function <float*> () {}
virtual float eval( unmodifiable<float*>::type x, unmodifiable<float*>::type y ) { ... }
// or just spell it out exactly, based on the variant of `unmodifiable` you've chosen, e.g.:
// virtual float eval (const float *x, const float *y) { ... }
};