vendredi 6 janvier 2012

Covariance

C'est étrangement une fonctionnalité qui semble relativement peu connue des programmeurs C++: la covariance des types de retours dans les fonctions surchargées.

L'exemple standard est la déclaration d'une méthode "clone", qui renvoie une copie de l'objet. Prenons une classe A:


class A
{
public:
virtual ~A();
virtual A * clone() const;
};

Maintenant, comment écrire une classe B dérivant de A, de manière à ce que la méthode clone fonctionne, c'est à dire renvoie une instance de B si le type sous-jacent est B? Ou plus exactement, quel devrait être le type de retour de la méthode clone de B?

L'on peut bien entendu retourner A. C'est clair, net et précis lorsque l'on travaille avec A, mais c'est dommage lorsque l'on travaille directement avec B: appeler clone renvoie un pointeur sur A, alors que l'on sait pertinemment que c'est un B, et on doit donc le caster vers B pour travailler avec.

Profitons donc de cette fonctionnalité introduite dans C++98, qui dit que si une méthode virtuelle renvoie un pointeur, l'on peut surcharger cette méthode avec un type de retour pointeur vers un objet dérivé. L'on écrira donc:

class B : public A
{
public:
B * clone() const;
};

Et la méthode clone fonctionnera exactement de la manière désirée.

Il est à noter que le type de retour peut-être différent du type parent: si A dérive de B, et C dérive de D, une méthode de A retournant C peut être surchargée dans B comme retournant D.

Il est à noter également que malheureusement, cette fonctionnalité devient de moins en moins utile: de nos jours, il est de plus en plus rare de renvoyer un pointeur à poil, et il n'est pas possible d'obtenir le même résultat avec un shared_ptr, par exemple. Dans ce cas, il faudra renvoyer systématiquement le type parent, et faire un cast.

Aucun commentaire: