为什么std::string在发布时是标准布局类型,但在调试时不是标准布局类型?

2024-05-13

#include <iostream>
#include <type_traits>
#include <string>
int main()
{
    std::cout << std::is_standard_layout<std::string>::value;
    system("pause");
    return 0;
}

我使用 Visual Studio 2017 来编译代码。当处于调试模式时,它输出 0。但是当处于发布模式时,它输出 1。是什么原因造成的?


以下是稍微简化的解释,因为下面描述的大多数类都是模板化的,为了简洁起见,我省略了这些细节。

在 MSVC 2017 工具集版本 14.16 中,std::string类源自_String_alloc类,它有一个私有数据成员_Mypair类型的_Compressed_pair。班上_Compressed_pair是从空派生的std::allocator<char>类,并且有一个私有的_Myval2类型的数据成员_String_val。班上_String_val源自_Container_base类,并且它具有三个私有数据成员:_Bx of a union type _Bxty(它能够为小字符串优化提供就地缓冲区,或为常规字符串提供指向动态分配缓冲区的指针),_Mysize and _Myres, 两者的size_type,跟踪字符串的长度和保留的存储大小。

In release模式,当_ITERATOR_DEBUG_LEVEL https://learn.microsoft.com/en-us/cpp/standard-library/iterator-debug-level is #defined as 0, the _Container_base是空的别名struct _Container_base0。然而,编译debug模式,当_ITERATOR_DEBUG_LEVEL is not 0, the _Container_base是非空的别名struct _Container_base12,其中包含单个公共数据成员_Myproxy(这是一个指向_Container_proxy结构)。

现在,这里有一个问题:其中之一标准布局类型的要求 https://en.cppreference.com/w/cpp/named_req/StandardLayoutType其所有(非静态)数据成员应在同一个类中声明(即,全部在最派生类本身中声明,或者全部在某个特定基类中声明,但不分散在不同的类中)。因此,由于上述_Container_base12辅助结构(用于调试目的),_String_val不再满足此要求,因此不能被视为标准布局类型。特别是,其_Myproxy数据成员声明于_Container_base12, 然而_Bx, _Mysize and _Myres数据成员直接声明在_String_val itself.

暗示着,当使用非零_ITERATOR_DEBUG_LEVEL, the std::string类本身不再满足标准布局类型要求,因为规则之一是所有(非静态)数据成员和基类本身也应是标准布局类型。

虽然这确实回答了您关于差异起源的问题,但请记住,这些只是未记录的、特定于实现的细节,不应依赖。 Microsoft 将来可能会更改/重组其库代码。例如,MSVC 2019 工具集版本 14.28 已经使用组合而不是继承:在那里,std::string类不是派生自_String_alloc不再,而是直接包含自己的单个_Mypair类型的数据成员_Compressed_pair。 (幸运的是,ABI兼容性 https://learn.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017工具集 v14.28 和 v14.16 之间仍然以这种方式保留,因为由此产生的内存布局std::string保持不变。)当然,MSVC 2019/工具集 v14.28 的推理类似std::string调试模式下不是标准布局。然而,某些未来的版本可能会带来重大变化,然后在某个时候也许std::is_standard_layout<std::string>::value在调试模式下观察到的甚至可能会更改为true不知不觉中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么std::string在发布时是标准布局类型,但在调试时不是标准布局类型? 的相关文章

随机推荐