With virtual member functions:

#include <iostream>

struct X {
    virtual void f() { std::cout << "X::f()\\n"; }
};

struct Y : X {

// Specifying virtual again here is optional // because it can be inferred from X::f(). virtual void f() { std::cout << “Y::f()\n”; }

};

void call(X& a) {
    a.f();
}

int main() {

X x; Y y; call(x); // outputs “X::f()” call(y); // outputs “Y::f()”

}

Without virtual member functions:

#include <iostream>

struct X {
   void f() { std::cout << "X::f()\\n"; }
};

struct Y : X {
   void f() { std::cout << "Y::f()\\n"; }
};

void call(X& a) {
    a.f();
}

int main() {

X x; Y y; call(x); // outputs “X::f()” call(y); // outputs “X::f()”

}