When I study about override
keyword, I found some strange thing like below code.
#include <iostream>
template <class T>
class A
{
public:
virtual void some_function(const T a)
{
std::cout<<__PRETTY_FUNCTION__<<std::endl;
std::cout<<"Base"<<std::endl;
}
};
class Derived : public A<int*>
{
public:
virtual void some_function(const int* a)
{
std::cout<<__PRETTY_FUNCTION__<<std::endl;
std::cout<<"Derived"<<std::endl;
}
};
int main()
{
A<int*>* p = new Derived;
p->some_function(nullptr);
delete p;
}
When I first saw that code, I expected "Derived" to be called. But above code print result like below.
void A<T>::some_function(T) [with T = int*]
Base
But when I removed const
keyword in the some_function
that placed in Derived
class,
class Derived : public A<int*>
{
public:
virtual void some_function(int* a)
{
std::cout<<__PRETTY_FUNCTION__<<std::endl;
std::cout<<"Derived"<<std::endl;
}
};
It print "Derived". Can you tell me why this is happening?
CodePudding user response:
The function prototypes are not the same. For T=int*
const T a
: a
is a const
pointer to an int
, while const int *a
is a pointer to an const int
. For one the pointer is const
, for the other the int
is const
.
int * const a
would be the same as const T a
, or you can make T=const int*
.
https://godbolt.org/z/WEaEoGh58
Also important to note: When you derive from a class
you must declare a virtual
destructor.
A hint: when you use the keyword override
on the derived function, you will get an error that you are not overriding the function: https://godbolt.org/z/o87GMrhsd
CodePudding user response:
At
const T a
const qualifier is interpreted as top-level cv-qualifier for T, so it is processed
T const a
thus if int* is set to T, parameter type becomes
int* const a
So, your Derived's function signature (it is called second level const qualifier)
const int* a
is different from Base's one. As a result, your Derived virtual function is not called.
In addition, C has the rule of "ignore top-level cv-qualifier from function signature". So, your Base's signature
int* const
and if you give the type of
int*
into Derived's signature, these two types signature are interpreted to be equivalent. As a result, your Derived virtual function is called correctly.
The following is a back-ground for your help. The type of
T*
has two component T and pointer and then
T* const
is called top-level cv-qualifier which is applied to *, and then
T const *
is called second level cv-qualifier which is applied to T (equal to const T*). C ignore top-level cv-qualifier from signature. On the other hand, second level qualifier is included to signature. Therefore for example,
void f(int*) {}
void f(int* const){}
is failed to compile because of same signature by top-level cv-qualifier ignoring. In contrast,
void f(int*) {}
void f(int const * ){}
is compiled successfully by the difference of second level cv-qualifiers.
This is important to understand the behavior of virtual function calls. The reason is that the signature of derived virtual function and base's one must be identical to call it correctly.