在这种情况下,std :: vector如何被初始化?

问题描述

我最近遇到了一小段代码,使我感到很奇怪。

#include <iostream>
#include <array>
#include <vector>

int main()
{
    std::vector<std::array<int,2>> idx;
    for (auto ii = 0; ii < 2 * 10; ii += 2)
        {
        idx.push_back ({ii,ii + 1});
        }

    auto &ind = reinterpret_cast<std::vector<int> &> (idx);   
    idx.push_back ({ 40,50 });

    for (auto ii: ind)
        {
        std::cout << ii << std::endl;
        }
    
}

我确定代码会给出错误的结果,但是它给了我预期的结果。 一些观察:

  1. 在这种情况下,reinterpret_cast的行为是不确定的。
  2. 这不是很好的代码
  3. 它在我测试过的所有编译器上均可工作。通过工作,我的意思是它给了我很好的结果。

话虽如此,我阅读了VS 2019的std :: vector实现。std::vector::size()的值是根据_Mylast - _Myfirst计算出来的。

我的问题是 _Mylast如何正确初始化?

我阅读了代码,但是找不到。这不是关于不确定行为的问题,而是关于C ++向量的实现。

解决方法

在此实现中,矢量获取大小的方法是:将指针指向最后一个元素,然后从该指针中减去指向数据开头的指针。该结果就是向量中元素的数量。当您使用idx执行此操作时,由于_Mylast11 * sizeof(std::array<int,2>)之间有_Myfirst个字节,因此得到11个。当您使用ind时,_Mylast_Myfirst具有相同的值,因此它们是相同的字节数,但是现在它们是int*而不是{{ 1}},这意味着编译器将根据std::array<int,2>*来处理减法运算,从而得出方程式

sizeof(int)

给出22的“正确”结果。就标准而言,这是所有未定义的行为,但这就是为什么它似乎具有正确的结果的原因。如果vector允许将大小存储为类成员(允许这样做),那么您将从size = 11 * sizeof(std::array<int,2>) / sizeof(int) idx中获得相同的值。

,
  1. UB的典型处理方法是什么都不做,并假设程序员知道他们在做什么,所以即使您错了,它也会采取最小的阻力之路,并继续做正确的事情。
  2. 典型的vector实现为三个指针:一个指向数据存储区(我们将称为datap),一个指向数据存储区的末尾,因此您可以轻松地知道何时要从头开始,需要重新分配以获取更大的数据存储区(称为capp),以及指向该数据存储区中使用的数据结尾的指针(endp)。
  3. 通常,元素的数量是一些通用指针算法endp - datap,它将提供endpdatap之间的元素数量。

Visual Studio遵循典型的实现,因此使用较小的数组,因为我懒于绘制整个该死的东西,所以让我们看一下vector<array<int,2>>的2个元素和4个容量。 / p>

+-------+-------+-------+-------+
|  1    |   2   |   X   |   X   |
+-------+-------+-------+-------+
^               ^               ^
datap           endp            capp

reinterpret_cast用不同的眼光看待事物。它不会更改任何值,并且由于vector的两个视图具有完全相同的成员,并且这些成员具有完全相同的大小datap,因为一个视图与datap位于同一位置对于其他。它将使用另一种类型来解释,在这种情况下,该类型的大小只有一半。 <vector<int>>看起来像

+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | X | X | X | X |
+---+---+---+---+---+---+---+---+
^               ^               ^
datap           endp            capp

大小和容量的两倍。

您可以使用更复杂的结构来执行此操作,但是可能会填充,vtables和其他有趣的东西,从而使包含的int不能完美对齐,这就是为什么人们说不要相信您的结果。 datapendpcapp将始终指向相同的位置,但是如果投射到vector<string>,则大小甚至可能为1。

+-----------------------+-------+
|           1           |
+-----------------------+-------+
^               ^               ^
datap           endp            capp

实际上,vector实现可能没有使用三个指针。 Visual Studio的下一个更新可以实现vector,其中包含用于数据的指针和用于容量和大小的两个整数。如果满足vector的要求,则某些天才可以用独角兽的角,凤凰羽毛和一堆Care Bear填充物实现。该实现也可能超出职责范围,并捕获错误并警告您(Visual Studio在vector的调试版本中正是这样做的,以捕获运算符[]中的越界访问,但是我不知道它怎么会用reinterpret_cast欺骗编译器或入侵五角大楼。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...