но я не могу сказать, действительно ли это ... законно.
GetData
и GetDataOtherWay
не являются «законными». Поведение программы не определено.
GetRowsOtherWay
само по себе хорошо, хотя, вы, вероятно, должны использовать ссылку вместо аргумента указателя. Если вы используете указатель, то вам, вероятно, следует проверить, что он не равен нулю.
Это просто "не в порядке" с контейнерами? Потому что повсюду в нашей кодовой базе я вижу, как люди приводят объектные ptrs в свои базовые классы, т.е.
Преобразование указателя в указатель на базу в порядке. Однако вектор производного объекта не является производным от вектора базового объекта. У Vector вообще нет базового класса.
Обратите внимание, что состав исполнителей здесь излишний и рискованный. Преобразование в базовый указатель неявно, поэтому GetThatRow(&mySuperRow)
работает так же хорошо. Если вы хотите сделать это явным, используйте static_cast
, чтобы компилятор обеспечил безопасность типов.
Это работает
Внешний вид «работает», как вы ожидали, является примером возможного неопределенного поведения.
Разве явное приведение объекта к его базовому классу не должно совпадать с явным приведением вектора объектов к вектору базового класса объектов?
Это не то же самое. Кроме того, здесь мы обсуждаем преобразование указателей (хотя и ссылок), а не преобразование объектов. Существует статическое преобразование из типа указателя в другой тип указателя в той же иерархии наследования. Не существует статического преобразования из указателя на один вектор в указатель на другой вектор. Эти типы не связаны через наследование.
Откуда мы знаем, что это UB?
Потому что стандарт так говорит (цитата из стандартного проекта):
[basic.lval]
Если программа пытается получить доступ к сохраненному значению объекта через
glvalue, тип которого не похож ([conv.qual]) на один из
следующие типы поведение не определено :
- динамический тип объекта,
- тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта, или
- тип char, unsigned char или std :: byte.
std::vector<Row>
не похож на динамический тип *rows
(то есть std::vector<SuperRow>
) и не похож на соответствующий тип со знаком или без знака и не похож на char
, unsigned char
или std::byte
.
Хорошее эмпирическое правило: избегайте повторного толкования приведения, а если вам это нужно, убедитесь, что вы знаете языковые правила, которые в нем участвуют, и не нарушаете их. И никогда не используйте приведение в стиле C, потому что оно переосмысливает приведение типа, независимо от того, предназначено оно для вас или нет.
Как создать вектор производных объектов из вектора базовых объектов: во-первых, вам нужна функция, которая создает производный объект из базы. Базовый объект обычно не конвертируется в его производные типы, хотя это можно сделать, добавив конвертирующий конструктор в производный тип. После того, как вы написали такую функцию, используйте std::transform
, чтобы применить ее к вектору баз.
Преобразование вектора производного в вектор основ проще, поскольку базовый подобъект может быть просто скопирован и возвращен. Эта операция называется нарезкой.