以下是稍微简化的解释,因为下面描述的大多数类都是模板化的,为了简洁起见,我省略了这些细节。
在 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 #define
d 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
不知不觉中。