【问题标题】:How reinterpret_cast works for flattening a std::vector?reinterpret_cast 如何用于展平 std::vector?
【发布时间】:2019-07-17 02:52:25
【问题描述】:

我想将包含多个整数的结构的std::vector 表示为整数的“扁平化” 向量,不复制数据

我用reinterpret_cast 尝试了一些东西,如下所示:

#include <vector>
#include <iostream>

struct Tuple
{
    int a, b, c;
};

int main()
{
    // init
    std::vector<Tuple> vec1(5);
    for(size_t i=0; i<vec1.size(); ++i)
    {
        vec1[i].a = 3 * i + 0;
        vec1[i].b = 3 * i + 1;
        vec1[i].c = 3 * i + 2;
    }   

    // flattening
    std::vector<int>* vec2 = reinterpret_cast<std::vector<int>*>(&vec1);

    // print
    std::cout << "vec1 (" << vec1.size() << ")  : ";
    for(size_t i=0; i<vec1.size(); ++i)
    {
        std::cout << vec1.at(i).a << " " << vec1.at(i).b << " " << vec1.at(i).c << " ";
    }
    std::cout << std::endl;

    std::cout << "vec2 (" << vec2->size() << ") : ";
    for (size_t j = 0; j < vec2->size(); ++j)
    {
        std::cout << vec2->at(j) << " ";
    }
    std::cout << std::endl;

    return 0;
}

效果很好,因为输出是:

vec1 (5)  : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 
vec2 (15) : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

我的问题是:

  • 此行为是否依赖于编译器? (我使用的是g++ 6.3.0
  • vec2 如何知道向量的大小是15 而不是5
  • 他们是否有任何其他解决方案避免使用reinterpret_cast? (如果我“不小心”double 成员添加到Tuple,则可能很难跟踪由此产生的问题...)
  • 如果vec1 有一个特定的分配器:std::vector&lt;Tuple,A&lt;Tuple&gt;&gt;,那么vec2 的类型应该是什么? std::vector&lt;int&gt;std::vector&lt;int,A&lt;int&gt;&gt;std::vector&lt;int,A&lt;Tuple&gt;&gt;

【问题讨论】:

  • 如果你必须这样做,你可以转换std::vector::data()的结果来获得指向向量管理的第一个元素的指针。
  • 这是未定义的行为。不保证便携,不保证工作。
  • 您可以将向量返回的 data() 指针重新解释转换为另一种类型,但不能将整个向量转换为另一种类型。即使您仅 reinterpet_cast 数据指针,它也只有在 pragma pack 设置为 1 时才可移植,否则 sizeof(struct Tuple) 可能大于 3 * sizeof(int)。
  • 不要这样做。如果您是初学者,您不应该使用reinterpret_cast&lt;&gt;。这段代码只是被破坏了,它工作的事实只是一个随机的幸运机会。更改任何内容(包括编译器标志)都可能导致它崩溃。
  • 你问它是如何知道它是 15 而不是 5。虽然代码是无效的,正如其他地方所涵盖的那样,但那个特定的位可能很有趣。向量不存储大小,它存储指向数据开始和结束的指针,大小返回,本质上是 end() - begin()。如果您想了解它最终如何工作godbolt.org/z/B1Uvq4 - 更改指针转换为的类型或结构中缓冲区的大小,它可能会变得更容易看到。

标签: c++ vector std allocator reinterpret-cast


【解决方案1】:

您不能合法地将整个向量重新解释为不同类型的向量。但是您可以合法地将指向结构的指针转换为指向该结构的第一个元素的指针。所以这行得通:

std::vector<Tuple> vec1(5);
int* vec2 = &vec1.front().a;
size_t vec2_size = vec1.size() * sizeof(vec1[0]) / sizeof(vec2[0]);

for (size_t j = 0; j < vec2_size; ++j)
{
    std::cout << vec2[j] << " ";
}

您需要确保Tuple 中没有填充,所以:

static_assert(sizeof(Tuple) == 3 * sizeof(int), "Tuple must be 3 ints");

要回答您的项目符号问题:

  • 此行为是否依赖于编译器?
    • 您的代码是非法的。
  • vec2 如何知道向量的大小是 15 而不是 5?
    • 你很幸运,你的代码是非法的。
  • 他们是否还有其他避免使用 reinterpret_cast 的解决方案?
    • 见上文。
  • 如果 vec1 有一个特定的分配器:std::vector>,那么 vec2 的类型应该是什么?
    • 同上,int*

【讨论】:

  • "但是您可以合法地将指向结构的指针转换为指向该结构的第一个元素的指针。" 如果您想获得技术知识,@ 隐含的指针算术987654325@ 是 UB,因为 vec2 指向单个整数对象,而不是整数数组。当然,这通常仍然有效,但您仍然应该知道您正在调用 UB。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多