Some notes on covariance and contravariance
Within the type system of a programming language, a typing rule or a type conversion operator is: covariant if it preserves the ordering, ≤, of types, which orders types from more specific to more generic; contravariant if it reverses this ordering, which orders types from more generic to more specific; invariant if neither of these apply.
In object oriented languages such as C++, if class B is a subtype of class A, then all member functions of B must return the same or narrower set of types as A; the return type is said to be covariant. On the other hand, the member functions of B must take the same or broader set of arguments compared with the member functions of A; the argument type is said to be contravariant. The problem for instances of B is how to be perfectly substitutable for instances of A. The only way to guarantee type safety and substitutability is to be equally or more liberal than A on inputs, and to be equally or more strict than A on outputs. - From Wikipedia
Examples from Lecture
Conversion operator Fraction Example - slides 16 & 17
#include <iostream> using namespace std; class Frac { public: Frac(int top, int bottom) { t = top; b = bottom; }; Frac(int top) { t = top; b = 1; }; Frac operator*(const Frac &right) { return Frac(t*right.t, b*right.b); }; operator double() { return (double) t / (double) b; }; void printFrac() { cout << t << " / " << b << endl; } private: int t; int b; }; int main() { Frac a(2, 3); a.printFrac(); Frac b(2, 2); a = a * b; a.printFrac(); double f = a; cout << "f=" << f << endl; double bVal = b; cout << "bVal=" << bVal <<endl; }
Exercises
Exercises 1: Overload +, - and / operators for the Frac class
Overload the +, - and / operators in the above Frac class to add, subtract and divide fractions.