C++
Ковариантность типа возврата
Поиск…
замечания
Ковариация параметра или возвращаемого значения для виртуальной функции-члена m
, где его тип T
становится более специфичным в переопределении производного класса m
. Тип T
затем изменяется ( дисперсия ) по специфичности таким же образом ( co ), что и классы, обеспечивающие m
. C ++ обеспечивает языковую поддержку для ковариантных типов возвращаемых данных, которые являются необработанными указателями или исходными ссылками - ковариация предназначена для типа адресата или референта.
Поддержка C ++ ограничена возвращаемыми типами, потому что возвращаемые значения функции являются единственными чистыми out-arguments в C ++, а ковариация - только тип safe для чистого аргумента. В противном случае вызывающий код может предоставить объект менее определенного типа, чем ожидает код приема. Профессор Массачусетского технологического института Барбара Лисков изучила эту проблему и связала ее с вопросами безопасности, и теперь она известна как Принцип замещения Лискова или LSP .
Ковариантная поддержка существенно помогает избежать опускания и динамического контроля типов.
Поскольку интеллектуальные указатели типа класса не может использовать встроенную поддержку для ковариации непосредственно для смарт результатов указателей, но можно определить по- видимому , общековариантную Непро- virtual
смарт - функцию результата указателя оболочки для ковариантной virtual
функции , которая производит сырье указателей.
1. Базовый пример без ковариантных возвратов, показывает, почему они желательны
// 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. Ковариантная версия результата базового примера, проверка статического типа.
// 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. Результат ковариантного умного указателя (автоматическая очистка).
// 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.
}