Как-то можно обойти ошибку, которую выдаёт компилятор, в следующем коде? Т.е. заставить его воспринимать унаследованный тип как базовый. Компилилось в 2008-й вижуал студии.
template <typename T>
class Templated {
...
};
class A {
...
};
class B : public A {
...
};
int main() {
// тут ошибка C2440, что нельзя преобразовать один тип к другому
Templated<A*>* obj = new Templated<B*>();
}
Здравствуйте, 24, Вы писали:
24>Как-то можно обойти ошибку, которую выдаёт компилятор, в следующем коде? Т.е. заставить его воспринимать унаследованный тип как базовый. Компилилось в 2008-й вижуал студии.
Придется написать руками соответствующий шаблонный конструктор, который будет содержать проверку типа, примерно так (не компилировал, пишу прямо в браузере):
Здравствуйте, 24, Вы писали:
24>Как-то можно обойти ошибку, которую выдаёт компилятор, в следующем коде? Т.е. заставить его воспринимать унаследованный тип как базовый.
Для указателей — Templated<B>* --> Templated<A>* — нужно, чтобы Templated<B> был унаследован от Templated<A>.
Это можно сделать, если иерархия прообразов (A,B) известна.
Либо явно, либо ввести метафункцию baseclass, определённую над прообразами.
template<class T> struct deftype { typedef T type; };
template<class T> struct baseclass : deftype<void> {};
struct A { ... };
struct B : A { ... };
template<> struct baseclass<B> : deftype<A> {};
struct C : B { ... };
template<> struct baseclass<C> : deftype<B> {};
.....
template<class T> struct Templated;
template<> struct Templated<void> {};
template<class T> struct Templated
: Templated<typename baseclass<T>::type>
{ ... };
// нужно ещё позаботиться, чтобы объекты не разраслись из-за наследования один от другого
Templated<A>* pta = new Templated<C>();
Для объектов же, Templated<B> --> Templated<A> — достаточно определить пользовательские приведения типов: конструкторами или операторами, по вкусу.
Пример — boost::shared_ptr, где воспроизводится такая же ковариантность, как у голых указателей.
Здравствуйте, 24, Вы писали:
24>Как-то можно обойти ошибку, которую выдаёт компилятор, в следующем коде? Т.е. заставить его воспринимать унаследованный тип как базовый. Компилилось в 2008-й вижуал студии.
24>
24>template <typename T>
24>class Templated {
24> ...
24>};
24>class A {
24> ...
24>};
24>class B : public A {
24> ...
24>};
struct _Empty;
template <typename _Ty>
struct _Templ_parser { struct _Empty result; };
template <typename _Ty_p, template <class> class _Ty_templ>
struct _Templ_parser<_Ty_templ<_Ty_p> >
{
typedef _Ty_p result;
};
template <typename _Ty_dst, typename _Ty_src>
struct _Converter
{
typedef _Empty result;
};
template <typename _Ty_dst, typename _Ty_src, template <class> class _Ty_templ>
struct _Converter<_Ty_dst, _Ty_templ<_Ty_src> >
{
typedef _Ty_templ<_Ty_dst> result;
};
template <typename _Dst, typename _Src>
typename _Converter<_Dst, _Src>::result* caster(_Src *ptr)
{
typedef _Templ_parser<_Src> _Par_type;
return boost::is_base_of<_Dst, _Par_type>::value ? reinterpret_cast<typename _Converter<_Dst, _Src>::result*>( ptr):0;
}
int main() {
// тут ошибка C2440, что нельзя преобразовать один тип к другому
Templated<A*>* obj = caster<A*>(new Templated<B*>());
}
24>
А вообще там неплохо еще всякие is_ptr преобразования (и не базироваться на пттрах) и большее обобщение кейсов и чуть подшаманить конвертер добавить конструктор и оператор преобразования к дст, в общем более изяшно реализовать идею
П,С, не компилил наверное есть ошибки
Compiler can be as trained AI but can't compose music.
Antheil piano jazz sonata. Я болен ПГМ.
J>va.push_back( new A ); // ну а чё, vector<A*> же
J>...
J>B* pb = vb.back(); // ой?
J>
Не ой, когда куда-то передаёшь кому-то вектор vector<A*> в режиме только на чтение.
Т.е. удобно в одном месте вектор подготовить, пользуясь более безопасной типизацией, зная что это B.
(В другом месте, например, уже С).
А пользователю передаём массив для чтения. И он уже пользуется только ограниченным интерфейсом A.
Но, как я понимаю, нормально реализовать это в C++ нельзя.
J>>va.push_back( new A ); // ну а чё, vector<A*> же
J>>...
J>>B* pb = vb.back(); // ой?
J>>
_>Не ой, когда куда-то передаёшь кому-то вектор vector<A*> в режиме только на чтение. _>Т.е. удобно в одном месте вектор подготовить, пользуясь более безопасной типизацией, зная что это B. _>(В другом месте, например, уже С). _>А пользователю передаём массив для чтения. И он уже пользуется только ограниченным интерфейсом A.
В случае вектора проще всего передавать пару указателей begin/end (через Boost.Range, например) — указатели можно кастить к чему угодно.
Еще вариант обёртки указателей — boost::multi_array_ref.
Еще вариант обёртки итераторов вообще: boost::transform_iterator.
_>Но, как я понимаю, нормально реализовать это в C++ нельзя.
Можно, но ты же понимаешь, это уже не совсем std::vector будет, раз у него интерфейс до read-only ограничивается. Так что тут специальный тип-адаптер нужен, как-то так (пишу в браузере, просто идею продемонстрировать):
По-хорошему надо свой тип кастящего итератора делать тоже, потому что, например, в STLPort в дебажной версии итераторы у вектора — это не указатели, а объекты, проверяющие выход за границы и т.п., и код выше для них не скомпилится.
В любом случае, в код, принимающий строго std::vector<A>, ничто из вышеупомянутого не залезет.
Надо либо писать на итераторах, тогда все будет работать с любым контейнером (но код итерации будет шаблонным), либо на паре указателей (random-access range), если не хочется разводить шаблоны.
С++ тут ни при чем, потому что можно легко представить себе случаи, когда автоматическая конверсия будет неправильной. Например, уже представление std::vector<B> в виде std::vector<A>, очевидно, даст неправильный результат, хотя для указателей оно работало бы.