【问题标题】:constexpr std::string in C++20, how does it work?C++ 20 中的 constexpr std::string,它是如何工作的?
【发布时间】:2022-01-03 21:30:02
【问题描述】:

显然,GCClibstdc++ 还没有添加 constexpr std::string(从 GCC v11.2 开始)。

这段代码:

#include <iostream>
#include <string>


int main( )
{
    constexpr std::string str { "Where is the constexpr std::string support?"};

    std::cout << str << '\n';
}

无法编译:

time_measure.cpp:37:31: error: the type 'const string' {aka 'const std::__cxx11::basic_string<char>'} of 'constexpr' variable 'str' is not literal
   37 |         constexpr std::string str { "Where is the constexpr std::string support?"};
      |                               ^~~
In file included from c:\mingw64\include\c++\11.2.0\string:55,
                 from c:\mingw64\include\c++\11.2.0\bits\locale_classes.h:40,
                 from c:\mingw64\include\c++\11.2.0\bits\ios_base.h:41,
                 from c:\mingw64\include\c++\11.2.0\ios:42,
                 from c:\mingw64\include\c++\11.2.0\ostream:38,
                 from c:\mingw64\include\c++\11.2.0\iostream:39,
                 from time_measure.cpp:2:
c:\mingw64\include\c++\11.2.0\bits\basic_string.h:85:11: note: 'std::__cxx11::basic_string<char>' is not literal because:
   85 |     class basic_string
      |           ^~~~~~~~~~~~
c:\mingw64\include\c++\11.2.0\bits\basic_string.h:85:11: note:   'std::__cxx11::basic_string<char>' does not have 'constexpr' destructor

我的问题是,当字符串包含超过 16 个chars(因为 GCC 的 SSO 缓冲区大小为 16)时,这些字符串将如何在后台工作?有人可以给我一个简短的解释吗? 一个简单的构造函数会在堆栈上创建字符串对象并且从不使用动态分配吗?

这段代码:

    std::cout << "is_trivially_constructible: "
              << std::boolalpha << std::is_trivially_constructible<const std::string>::value << '\n';

打印这个:

is_trivially_constructible: false

现在在这里使用constexpr(显然不能使用 GCC v11.2 编译):

    std::cout << "is_trivially_constructible: "
              << std::boolalpha << std::is_trivially_constructible<constexpr std::string>::value << '\n';

结果会是true,如下所示吗?

is_trivially_constructible: true

我的目标

我的目标是做类似的事情:

    constexpr std::size_t a { 4 };
    constexpr std::size_t b { 5 };
    constexpr std::string msg { std::format( "{0} + {1} == {2}", a, b, a + b ) };

    std::cout << msg << '\n';

std::formatconstexpr std::string 都不能在 GCC v11.2 上编译。

【问题讨论】:

  • 一个简单的构造函数会在堆栈上创建字符串对象并且从不使用动态分配吗? 没有必要,因为在常量表达式中允许动态分配,只要在持续评估结束之前,它全部被释放。
  • c++20 增加了将 constexpr 与分配器一起使用的能力 cppstories.com/2021/constexpr-new-cpp20
  • 该变量声明无效。 std::string 将在持续评估中可用。但它并不是神奇的文字类型。
  • @Artyer - 明确?你不能对它的实现做任何假设。
  • @Artyer - 您忽略了对其数据成员的约束。标准没有强加为文字类型。再说一遍,除非它是明确的字面意思,否则你不能这样宣称它。

标签: c++ c++20 constexpr stdstring


【解决方案1】:

C++20 支持在 constexpr 时间内分配,只要分配在时间常数评估结束时完全解除分配。因此,例如,这个非常愚蠢的例子在 C++20 中是有效的:

constexpr int f() {
    int* p = new int(42);
    int v = *p;
    delete p;
    return v;
}

static_assert(f() == 42);

但是,如果你忘记了delete p;,那么f() 就不再是一个常量表达式。不能泄露内存。例如,gcc 拒绝:

<source>:2:24: error: '(f() == 42)' is not a constant expression because allocated storage has not been deallocated
    2 |     int* p = new int(42);
      |                        ^

回到你的问题,std::string 可以在constexpr 中正常工作——正如你所期望的那样为它分配内存。但是,C++20 constexpr 规则仍然受此规则的限制,即所有分配必须在评估结束时清理。或者说,所有分配都必须是 transient - C++ 尚不支持非瞬态 constexpr 分配。

因此,您的原始程序

int main( )
{
    constexpr std::string str { "Where is the constexpr std::string support?"};
}

无效,即使 gcc 支持 constexpr string(就像它现在在主干上所做的那样),因为 str 需要被销毁。不过这样就好了:

constexpr int f() {
    std::string s = "Where is the constexpr std::string support?";
    return s.size();
}

static_assert(f() > 16);

而它不会在 C++17 中编译。


在 C++23 中仍然不支持非瞬态 constexpr 分配。这是一个令人惊讶的棘手问题。但是,希望很快。

【讨论】:

  • 哦,C++23 已经冻结了吗?我宁愿希望非瞬态 constexpr 分配能在 :(
  • @cigien 还没有,但还没有任何更新,所以目前看来不太可能。
  • 感谢您的信息。这让我有点难过。现在你能告诉我一种连接几个字符串文字和几个constexpr整数并将它们存储在编译时评估string/char*的方法吗?我想生成一条在运行时不会被修改的简单消息。我希望此消息由编译器生成并存储在可执行文件的只读段中,这样它就不会在运行时多次生成。
  • @Deduplicator constexpr 字符串无论如何都不能使用 SSO,它们总是会分配。
  • @Deduplicator 这取决于 SSO 实现是否基于重新解释转换。如果是,必须分配。 libstdc++ 显然不需要,所以不需要。
猜你喜欢
  • 1970-01-01
  • 2020-12-22
  • 2019-12-01
  • 2019-12-27
  • 2021-01-10
  • 2022-01-19
  • 1970-01-01
  • 2011-03-11
相关资源
最近更新 更多