C++20 中的 constexpr std::string,它是如何工作的?

constexpr std::string in C++20, how does it work?

显然,constexpr std::string 还没有添加到 GCC[=50] 的 libstdc++ =] 然而(从 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++.2.0\string:55,
                 from c:\mingw64\include\c++.2.0\bits\locale_classes.h:40,
                 from c:\mingw64\include\c++.2.0\bits\ios_base.h:41,
                 from c:\mingw64\include\c++.2.0\ios:42,
                 from c:\mingw64\include\c++.2.0\ostream:38,
                 from c:\mingw64\include\c++.2.0\iostream:39,
                 from time_measure.cpp:2:
c:\mingw64\include\c++.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++.2.0\bits\basic_string.h:85:11: note:   'std::__cxx11::basic_string<char>' does not have 'constexpr' destructor

我的问题是,当一个字符串包含超过 16 个 char 时(因为 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 时间内分配,只要分配在时间常量计算结束时完全解除分配即可。因此,例如,这个非常愚蠢的示例在 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(就像现在在 trunk 上所做的那样),因为 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 分配。这是一个非常棘手的问题。但是,希望很快。