为什么std::wstring().capacity()的constexpr不等于std::wstring().capacity()?

13

我不确定是我太天真还是太无知。

但为什么下面的内容不同呢?

constexpr auto nInitialCapacity1 = std::wstring().capacity();
const auto     nInitialCapacity2 = std::wstring().capacity();
在 Visual Studio 2022/17.0.5 上,上面的代码会产生以下结果:
nInitialCapacity1 = 8
nInitialCapacity2 = 7

为什么使用 constexpr(编译时)版本的结果与调用的 const 版本不相等?

感谢任何解释!


7
我认为标准库在运行时和编译时实现这个功能的方式并没有必要完全一致。毕竟默认构造的字符串容量已经取决于具体实现了。 - user17732522
1
@user17732522你说得很对。所有的实现都是特定的!但是为什么编译时版本应该与其他版本不同呢?无论哪个实现返回什么数字,我个人都希望得到相同的结果。 - Martin Lemburg
3
或许他们无法以通常的方式执行SSO,因为在常量表达式中无法重新输入内存,尽管基于联合体的实现也应该可以在常量表达式中工作。 - user17732522
@Brandon libstdc++(gcc)仅从版本12(即当前主干)开始支持它,而libc++(clang)尚未支持它。但是在C++20中应该是可能的。请参见https://en.cppreference.com/w/cpp/compiler_support(“constexpr std :: string”) - user17732522
3
在代码中添加-std=c++20后就可以正常工作。 - IlCapitano
显示剩余3条评论
2个回答

21

Microsoft的STL在常量求值上禁用了短字符串优化,因此它会分配内存。

分配的内存大小总是比一个2的幂次多1,所以容量(不包括最后的L'\0')总是2的幂次。

在非常量求值版本中,短字符串缓冲区可以容纳8个字符,其中一个是L'\0',因此容量为7。


1
无论哪种情况,is_constant_evaluated() 都应该返回 true - Barry
1
@Barry 是的,在这种情况下,因为它是const std::size_t,它将由常量表达式初始化,并且const auto x = std::wstring().capacity();确实会给出x = 8(在我的 Visual Studio 版本中),但我假设原始提问者没有显式地使用常量求值表达式,因为他们的问题标题如此命名。 - Artyer
昨天我和一位同事发现,在VS2202中,调试器在nInitialCapacity2的工具提示中可视化数字7,但在反汇编窗口中我们看到的值是8。即使使用TRACE1进行输出,也打印出7而不是8。我的同事的假设是,调试器不知道const(expr)或non-const(expr)调用的不同行为,只调用constexpr声明的方法non-const(expr),因为它没有副作用。在这种情况下,调试器得到了一个7并将其可视化为调试时的值。我们对这种行为感到非常惊讶! - Martin Lemburg

0

根据新的经验和观察进行更新:

  • 反汇编窗口的内容显示,const-call to std::wstring::capacity 的结果是8
  • 但是监视窗口和变量的工具提示显示7

一位同事的假设是,调试器调用了constexpr方法capacity non-const,得到了不同的结果7并将其可视化。

查看反汇编窗口的原因是以下代码的意外行为:

const auto nInitCap = std::wstring().capacity();
const auto nCap     = str.capacity();

if (nCap != nInitCap)
    std::wcout << "capacity " << nCap << "is not equal to the initial capacity " << nInitCap << std::endl;

if (nCap > nInitCap)
    std::wcout << "capacity " << nCap << "is greater than the initial capacity " << nInitCap << std::endl;

调试器显示了变量:

nInitCap: 7
nCap:     7

但是代码输出:

容量为7与初始容量7不相等

对于const构造的临时对象的容量调用返回8,如在反汇编中所见,因此行为是可以解释的,即使VS2022 17.0.5的调试器显示为7。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接