Consider the following two structures:
struct Potential{
virtual double energy(int full_system, int particle_id)=0;
double energy(int full_system){
int n_particles=100;
double E=0.0;
for (int particle_id=0; particle_id<n_particles; particle_id++){
E+=energy(full_system, particle_id);
}
return E;
}
};
struct MorsePotential: Potential{
//constructor
MorsePotential (){}
//implementing the virtual function
double energy(int full_system, int particle_id){
return 10.0;
}
};
The first one, Potential
has two functions, both called energy
. The first is virtual and should be implemented in all child structures. The second should be inherited as is by all children. Note that the second calls the first one in the parent.
MorsePotential
is a child of Potential
and implements the virtual energy
function from its parent. When using MorsePotential
, however, I am getting a compilation error. The following code:
MorsePotential mpotential;
std::cout<<mpotential.energy(0,0)<<std::endl; //works
std::cout<<mpotential.energy(0)<<std::endl; //gives compiler error
gives the error:
inherit.cc: In function ‘int main()’:
inherit.cc:35:32: error: no matching function for call to ‘MorsePotential::energy(int)’
35 | std::cout<<mpotential.energy(0)<<std::endl;
| ^
inherit.cc:25:9: note: candidate: ‘virtual double MorsePotential::energy(int, int)’
25 | double energy(int full_system, int particle_id){
| ^~~~~~
inherit.cc:25:9: note: candidate expects 2 arguments, 1 provided
I am confused because the first line compiles and I can call mpotential.energy(0,0)
, i.e. the implementation of the virtual function. The child, however, is unable to find mpotential.energy(0)
, i.e. the function implemented in its parent class.
I was wondering what causes this error? At first I thought that it may not be allowed to call virtual functions in the parent class, but defining MorsePotential mpotential;
compiles so it seems to be able to instantiate the child.
This is how unqualified name lookup works. Name lookup does not consider the arguments. It only looks for the name of the called function. You call energy
, MorsePotential::energy
is found, name lookup stops before the base class scope is considered.
You can qualify the name, to select the method from the base, by calling mpotential.Potential::energy(0);
.
Or you can pull the name from the base class to the derived class scope via a using declaration:
struct MorsePotential: Potential{
using Potential::energy; // <-------
//implementing the virtual function
double energy(int full_system, int particle_id){
return 10.0;
}
};
(You should not implement a constructor that does the same as the compiler generated one.)
Note that if you use actual polymorphism via a reference, the issue does not arise:
void foo(Potential& p) {
p.energy(42); // OK, also with your code
}