C++
Type de retour Covariance
Recherche…
Remarques
La covariance d'un paramètre ou d'une valeur de retour pour une fonction membre virtuelle m
est celle où son type T
devient plus spécifique dans une substitution de classe dérivée de m
. Le type T
varie alors ( variance ) en spécificité de la même manière ( co ) que les classes fournissant m
. C ++ fournit un support de langage pour les types de retour covariants qui sont des pointeurs bruts ou des références brutes - la covariance est pour le type pointee ou référent.
La prise en charge de C ++ est limitée aux types de retour car les valeurs de retour de fonction sont les seuls arguments de sortie purs en C ++, et la covariance est uniquement de type sûr pour un argument de sortie pur. Sinon, le code appelant pourrait fournir un objet de type moins spécifique que celui attendu par le code de réception. Barbara Liskov, professeur au MIT, a étudié cette question et les problèmes de sécurité liés à la variance, et elle est maintenant connue sous le nom de principe de substitution Liskov ou LSP .
La prise en charge de la covariance permet essentiellement d’éviter le downcasting et la vérification de type dynamique.
Étant donné que les pointeurs intelligents sont de type classe on ne peut pas utiliser directement le support intégré pour covariance intelligents résultats de pointeur, mais on peut définir apparemment covariants non virtual
fonctions wrapper intelligentes de résultat de pointeur pour une covariante virtual
fonction qui produit des pointeurs premières.
1. Exemple de base sans retours covariants, montre pourquoi ils sont souhaitables
// 1. Base example not using language support for covariance, dynamic type checking.
class Top
{
public:
virtual Top* clone() const = 0;
virtual ~Top() = default; // Necessary for `delete` via Top*.
};
class D : public Top
{
public:
Top* clone() const override
{ return new D( *this ); }
};
class DD : public D
{
private:
int answer_ = 42;
public:
int answer() const
{ return answer_;}
Top* clone() const override
{ return new DD( *this ); }
};
#include <assert.h>
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
cout << boolalpha;
DD* p1 = new DD();
Top* p2 = p1->clone();
bool const correct_dynamic_type = (typeid( *p2 ) == typeid( DD ));
cout << correct_dynamic_type << endl; // "true"
assert( correct_dynamic_type ); // This is essentially dynamic type checking. :(
auto p2_most_derived = static_cast<DD*>( p2 );
cout << p2_most_derived->answer() << endl; // "42"
delete p2;
delete p1;
}
2. Version du résultat de covariant de l'exemple de base, vérification de type statique.
// 2. Covariant result version of the base example, static type checking.
class Top
{
public:
virtual Top* clone() const = 0;
virtual ~Top() = default; // Necessary for `delete` via Top*.
};
class D : public Top
{
public:
D* /* ← Covariant return */ clone() const override
{ return new D( *this ); }
};
class DD : public D
{
private:
int answer_ = 42;
public:
int answer() const
{ return answer_;}
DD* /* ← Covariant return */ clone() const override
{ return new DD( *this ); }
};
#include <iostream>
using namespace std;
int main()
{
DD* p1 = new DD();
DD* p2 = p1->clone();
// Correct dynamic type DD for *p2 is guaranteed by the static type checking.
cout << p2->answer() << endl; // "42"
delete p2;
delete p1;
}
3. Résultat du pointeur intelligent covariant (nettoyage automatique).
// 3. Covariant smart pointer result (automated cleanup).
#include <memory>
using std::unique_ptr;
template< class Type >
auto up( Type* p ) { return unique_ptr<Type>( p ); }
class Top
{
private:
virtual Top* virtual_clone() const = 0;
public:
unique_ptr<Top> clone() const
{ return up( virtual_clone() ); }
virtual ~Top() = default; // Necessary for `delete` via Top*.
};
class D : public Top
{
private:
D* /* ← Covariant return */ virtual_clone() const override
{ return new D( *this ); }
public:
unique_ptr<D> /* ← Apparent covariant return */ clone() const
{ return up( virtual_clone() ); }
};
class DD : public D
{
private:
int answer_ = 42;
DD* /* ← Covariant return */ virtual_clone() const override
{ return new DD( *this ); }
public:
int answer() const
{ return answer_;}
unique_ptr<DD> /* ← Apparent covariant return */ clone() const
{ return up( virtual_clone() ); }
};
#include <iostream>
using namespace std;
int main()
{
auto p1 = unique_ptr<DD>(new DD());
auto p2 = p1->clone();
// Correct dynamic type DD for *p2 is guaranteed by the static type checking.
cout << p2->answer() << endl; // "42"
// Cleanup is automated via unique_ptr.
}