Приведение векторного адреса для копирования данных - PullRequest
0 голосов
/ 10 июля 2019

Я написал операцию, которая, кажется, работает нормально, но я не могу сказать, действительно ли она ... законна.

У меня есть класс строки и расширение

class Row {};
class SuperRow : public Row {};

У меня есть функция, которая возвращает вектор

std::vector<Row> GetRows(){
    Row row1;
    Row row2;
    std::vector<Row> rows;
    rows.push_back(row1);
    rows.push_back(row2);
    return rows;
}

У меня есть другая функция, которая хочет эти данные, но у нее есть вектор объектов, который расширяет тип данных основного вектора.

void GetData(std::vector<SuperRow> *rows){
    // in order to compile, must do this
    *((std::vector<Row>*)rows) = GetRows();
}

Это работает, но я смотрю на это, как будто я делаю что-то не так .. [править: хорошо, ясно, что это ужасно]


Я тоже, может быть, делаю то же самое по-другому ..

void GetRowsOtherWay(std::vector<Row> *rows){
   *rows = GetRows();
}

void GetDataOtherWay(std::vector<SuperRow> *rows){
    GetRowsOtherWay((std::vector<Row>*)rows);
}

Это тоже плохо?

1 Ответ

2 голосов
/ 10 июля 2019

но я не могу сказать, действительно ли это ... законно.

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, чтобы применить ее к вектору баз.

Преобразование вектора производного в вектор основ проще, поскольку базовый подобъект может быть просто скопирован и возвращен. Эта операция называется нарезкой.

...