constexpr std::string
update
constexpr std::string
updateLast month I posted “Just how constexpr
is C++20’s std::string
?” (2023-09-08),
showing some surprising behavior in libc++. As of a week ago, that surprising behavior
is gone!
The same day my post hit Hacker News, James Y. Knight filed libstdc++ bug 111351 asking for libstdc++ to match libc++’s more conservative behavior by disabling its SSO at constexpr time. After some pushback there, Knight (quite awesomely, in my view) pivoted to create not only a bug report but an actual patch for libc++ completely implementing constexpr SSO! His pull request was opened 2023-09-16 and merged into trunk on 2023-10-10.
We can even see the change immediately benefiting projects that consume Clang nightlies.
For example: Clang can detect unused global static
variables only when their initializers are
constant-expressions (because dynamic initialization and destruction themselves count as “uses”
that disguise the otherwise-unusedness of the variable).
So Clang is now able to detect unused
static const std::string
globals up to 22 characters in length —
see Chromium commit 879cb86.
The main point of my post (the “firewall” between constexpr evaluation and runtime execution) is still as valid as ever — perhaps even more so, now that libc++’s special cases are eliminated. But I thought it would be worth revisiting each of that post’s example snippets with libc++ trunk and seeing how they’ve changed.
constexpr std::string s = "William Shakespeare";
This 19-character constinit string is now legal on libc++ (though not on libstdc++ nor Microsoft STL, because their SSO limit is lower).
constexpr std::vector<int> v1 = {1, 2, 3};
constexpr std::vector<int> v2 = {};
v1
remains invalid and v2
remains valid, of course.
But now we also have:
constexpr std::string s1 = {'1', '2', '3'};
constexpr std::string s2 = {};
Both s1
and s2
are now valid. Before the patch, libc++ considered them both
invalid. (“Both?” Yes; remember that even an empty string
requires storage for
its trailing null byte. Last month’s libc++ refused to use SSO even for that one byte.
This is now fixed.)
libstdc++ rejects the following code (Godbolt)
due to its pointer-into-self string
representation, while Microsoft and now also libc++ accept.
int main() {
static constexpr std::string abc = "abc"; // OK
constexpr std::string def = "def"; // Error!
}
Finally:
constinit std::string s = "";
This definition (at global scope) is now accepted by all three vendors.