【问题标题】:Why is constexpr of std::wstring().capacity() not equal to std::wstring().capacity()?为什么 std::wstring().capacity() 的 constexpr 不等于 std::wstring().capacity()?
【发布时间】:2022-02-01 19:14:15
【问题描述】:

我不确定我是太天真还是太不了解。

但是为什么以下不同呢?

constexpr auto nInitialCapacity1 = std::wstring().capacity();
const auto     nInitialCapacity2 = std::wstring().capacity();

在 Visual Studio 2022/17.0.5 中,上面的代码导致:

nInitialCapacity1 = 8
nInitialCapacity2 = 7

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

感谢您的解释!

【问题讨论】:

  • 我看不出标准库需要在运行时和编译时以同样的方式实现这一点的任何理由。无论如何,默认构造的字符串的容量取决于实现。
  • @user17732522 你说的很对。所有具体实现!但是为什么编译时版本应该与其他版本不同呢?无论如何,实现将返回什么数字,我个人希望得到相同的结果。
  • 也许他们不能以通常的方式进行 SSO,因为无法在常量表达式中重新键入内存,尽管基于联合的实现也应该在常量表达式中工作。
  • constexpr auto nInitialCapacity1 = std::wstring().capacity(); 不适用于 ClangGCC-std=c++20-std=c++2aerror: constexpr variable 'nInitialCapacity1' must be initialized by a constant expression

标签: c++ visual-c++ stl constexpr visual-studio-2022


【解决方案1】:

Microsoft 的 STL disables short string optimisation in constant evaluated contexts,因此它改为分配内存。

分配总是大于二的幂,因此容量(不包括最后一个L'\0')总是二的幂。

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

【讨论】:

  • is_constant_evaluated() 在这两种情况下都应该是 true
  • @Barry 在这种情况下是的,因为它是 const std::size_t,它将由常量表达式初始化,并且 const auto x = std::wstring().capacity(); 确实给出了 x = 8(在我的 Visual Studio 版本中),但我'我假设原始提问者在给定问题标题的情况下没有明显的常数评估表达式
  • 昨天和同事发现,在VS2202中,调试器在nInitialCapacity2的工具提示中的watch中可视化了数字7,但是在反汇编窗口中我们看到了值8。即使使用TRACE1进行输出,打印出 7 而不是 8。我同事的假设是,调试器不知道 const(expr) 或 non-conts(expr) 调用的不同行为,只调用 constexpr 声明的方法 non-conts(expr) 因为它没有副作用。在这种情况下,调试器得到一个 7 并在调试时将 7 可视化为值。我们对这种行为感到非常惊讶!
【解决方案2】:

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

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

一位同事的假设是,调试器调用constexpr方法容量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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多