转换向量地址以复制数据

Casting a vector address to copy data

我写了一个看起来运行良好的操作,但我不知道它是否真的..合法。

我有一排class,还有分机

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

我有一个函数returns一个向量

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);
}

这也不好吗?

but I can't tell if it is really .. legal.

GetDataGetDataOtherWay 不是 "legal"。程序的行为未定义。

GetRowsOtherWay 本身很好,但您可能应该使用引用而不是指针参数。如果您确实使用指针,那么您可能应该检查它是否不为空。

Is this just "not okay" with containers? Because everywhere in our code base I see people casting object ptrs to their base classes, ie GetThatRow((Row*)&mySuperRow);

将指针转换为指向基的指针是可以的。但是,派生对象的向量不是从基础对象的向量派生的。 Vector 根本没有基数 class。

请注意,那里的演员表是多余的和有风险的。到基指针的转换是隐式的,所以 GetThatRow(&mySuperRow) 也能正常工作。如果您想使其显式化,请使用 static_cast 以便编译器强制执行类型安全。

This works

如您所料 "working" 的出现是一个可能的未定义行为的示例。


Shouldn't explicitly casting an object to its base class be the same as explicitly casting a vector of objects to a vector of the objects base class?

不一样。此外,我们在这里讨论的是指针的转换(尽管也适用于引用),而不是对象的转换。在同一继承层次结构中存在从指针类型到另一种指针类型的静态转换。不存在从指向一个向量的指针到指向另一个向量的指针的静态转换。这些类型不通过继承相关。

How do we know this is UB?

因为标准是这么说的(引自标准草案):

[basic.lval]

If a program attempts to access the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
  • a char, unsigned char, or std::byte type.

std::vector<Row>既不类似于*rows的动态类型(即std::vector<SuperRow>)也不类似于有符号或无符号的对应类型,也不类似于charunsigned charstd::byte.

一个好的经验法则:避免重新解释转换,如果需要,请确保您了解所涉及的语言规则,并且您没有违反它们。永远不要使用 C-style cast,因为无论您是否有意,它都会重新解释 casting。


如何从基础对象的向量创建派生对象的向量:首先,您需要一个从基础创建派生对象的函数。基对象通常不能转换为其派生类型,尽管可以通过向派生类型添加转换构造函数来实现。编写此类函数后,使用 std::transform 将其应用于碱基向量。

将派生向量转换为基向量更简单,因为可以简单地复制和返回基子对象。此操作称为切片。