C++ constexpr std ::字符串文字数组

发布于 2025-01-21 12:21:42 字数 491 浏览 0 评论 0原文

一段时间以来,我一直很高兴地使用代码中恒定字符串文字的以下风格,而没有真正理解它的工作原理:

constexpr std::array myStrings = { "one", "two", "three" };

这似乎很微不足道,但是我对引擎盖下发生的事情的细节感到沮丧。从我的理解来看,类模板参数扣除(CTAD)用于构造适当大小和元素类型的数组。我的问题是:

  1. 在这种情况下,std ::数组的元素类型是什么,还是该实现是特定的?查看调试器(我使用的是Microsoft C ++),这些元素只是指向非连续位置的指示。
  2. 以这种方式声明字符串文字的ConstexPR数组安全吗?

我可以做到这一点,但这并不那么整洁:

const std::array<std::string, 3> myOtherStrings = { "one", "two", "three" };

I've been happily using the following style of constant string literals in my code for awhile, without really understanding how it works:

constexpr std::array myStrings = { "one", "two", "three" };

This may seem trivial, but I'm hazy on the details of what is going on under the hood. From my understanding, class template argument deduction (CTAD) is used to construct an array of the appropriate size and element type. My questions would be:

  1. What is the element type of the std::array in this case, or is this implementation specific? Looking at the debugger (I'm using Microsoft C++), the elements are just pointers to non-contiguous locations.
  2. Is it safe to declare constexpr arrays of string literals in this way?

I could do this instead, but it's not as tidy:

const std::array<std::string, 3> myOtherStrings = { "one", "two", "three" };

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一抹苦笑 2025-01-28 12:21:42

AS user17732522已经注意到,原始代码的类型扣除产生了const> const std :: array&const; ,3&gt;。这有效,但它不是C ++ std :: String,因此每个用途都需要扫描nul terminator,它们不能包含嵌入式nul < /代码> s。我只是想强调我的评论中的建议,以使用std :: string_view

由于std :: String固有地依赖运行时内存分配,因此除非关联代码的 emalty 也是constexpr,否则不能使用它。 >(因此在运行时根本没有实际字符串 S,编译器在编译时计算最终结果),如果目标是避免不必要的运行时工作,则不太可能在这里为您提供帮助对于在编译时部分知道的事物(尤其是在每个函数调用上重新创建array时;它不是全局或static,所以它已经完成了很多次,而不仅仅是初始化一次使用之前)。

也就是说,如果您可以依靠C ++ 17,则可以用std :: string_view将差异分开。它具有非常简洁的文字形式(添加sv作为任何字符串字符字面的前缀),并且是完全constexpr,因此,您可以做到:

// Top of file
#include <string_view>
// Use one of your choice:
using namespace std::literals; // Enables all literals
using namespace std::string_view_literals; // Enables sv suffix only
using namespace std::literals::string_view_literals; // Enables sv suffix only

// Point of use
constexpr std::array myStrings = { "one"sv, "two"sv, "three"sv };

您得到的东西无涉及运行时工作,具有std :: String的大多数好处(知道其自身长度,可以包含嵌入式nul s,大多数面向字符串的API接受),因此可以操作更多高效比C风格的字符串对函数接受字符串数据的三种常见方式:

  1. 对于需要读取字符串式东西的现代API,它们接受std :: string_view by Value,而开销为只需将指针和长度复制到
  2. 接受const std :: string&amp;的旧API的函数,它将构造临时std :: string时,可以使用从std :: string_view提取长度的构造函数,因此它不需要在strlen中预先浏览C风格的字符串即可确定分配多少分配。
  3. 对于需要std :: String的任何API(因为它将修改/存储自己的副本),他们正在接收string by Value,并且您会获得相同的好处就像在#2中一样(必须构建,但更有效地构建)。

您使用std :: String_view s的唯一情况比使用std :: String是情况#2 /code>包含std :: String s,不会发生任何副本),只有在拨打几个这样的电话时,您才会丢失在那里;在这种情况下,您只需咬住子弹并使用const std :: array mystrings = {“一个“ s,”两个“ s,”三个“ s};,支付次要运行时成本要构建真实的字符串 s,以换取传递const std :: string&amp;时避免副本。

As user17732522 already noted, the type deduction for your original code produces a const std::array<const char*, 3>. This works, but it's not a C++ std::string, so every use needs to scan for the NUL terminator, and they can't contain embedded NULs. I just wanted to emphasize the suggestion from my comment to use std::string_view.

Since std::string inherently relies on run-time memory allocation, you can't use it unless the entirety of the associated code is also constexpr (so no actual strings exist at all at run-time, the compiler computes the final result at compile-time), and that's unlikely to help you here if the goal is to avoid unnecessary runtime work for something that is partially known at compile time (especially if the array gets recreated on each function call; it's not global or static, so it's done many times, not just initialized once before use).

That said, if you can rely on C++17, you can split the difference with std::string_view. It's got a very concise literal form (add sv as a prefix to any string literal), and it's fully constexpr, so by doing:

// Top of file
#include <string_view>
// Use one of your choice:
using namespace std::literals; // Enables all literals
using namespace std::string_view_literals; // Enables sv suffix only
using namespace std::literals::string_view_literals; // Enables sv suffix only

// Point of use
constexpr std::array myStrings = { "one"sv, "two"sv, "three"sv };

you get something that involves no runtime work, has most of the benefits of std::string (knows its own length, can contain embedded NULs, accepted by most string-oriented APIs), and therefore operates more efficiently than a C-style string for the three common ways a function accepts string data:

  1. For modern APIs that need to read a string-like thing, they accept std::string_view by value and the overhead is just copying the pointer and length to the function
  2. For older APIs that accept const std::string&, it constructs a temporary std::string when you call it, but it can use the constructor that extracts the length from the std::string_view so it doesn't need to prewalk a C-style string with strlen to figure out how much to allocate.
  3. For any API that needs a std::string (because it will modify/store its own copy), they're receiving string by value, and you get the same benefit as in #2 (it must be built, but it's built more efficiently).

The only case where you do worse by using std::string_views than using std::string is case #2 (where if the std::array contained std::strings, no copies would occur), and you only lose there if you make several such calls; in that scenario, you'd just bite the bullet and use const std::array myStrings = { "one"s, "two"s, "three"s };, paying the minor runtime cost to build real strings in exchange for avoiding copies when passing to old-style APIs taking const std::string&.

你又不是我 2025-01-28 12:21:42

是的,这是为您推论模板参数的CTAD。 (由于C ++ 17)

std :: array具有扣除指南可以使用这种形式的初始化器。

它将将mystrings的类型推论为

const std::array<const char*, 3>

const char*是将通常的数组到点衰减应用于初始化程序列表的元素的结果(它们是数组const char s)。

const前面是constexpr的结果。

数组的每个元素将指向相应的字符串文字。

constexpr是安全的,您可以使用数组元素,就像通过const cont char*指针一样的单个字符串文字。特别是尝试通过const_cast修改这些文字或数组的行为将具有不确定的行为。

const std :: array&lt; std :: string,3&gt;也有效,但在恒定表达式中不可用。 constexpr由于std:String而不允许使用。

CTAD也可以在字符串文字操作员的帮助下推导这种类型:

#include<string>
using namespace std::string_literals;

//...

const std::array myOtherStrings = { "one"s, "two"s, "three"s };

或因为C ++ 20:

const auto myOtherStrings = std::to_array<std::string>({ "one", "two", "three" });

Yes, this is CTAD deducing the template arguments for you. (since C++17)

std::array has a deduction guide which enables CTAD with this form of initializer.

It will deduce the type of myStrings to

const std::array<const char*, 3>

The const char* is the result of usual array-to-pointer decay being applied to the elements of the initializer list (which are arrays of const chars).

const in front is a consequence of constexpr.

Each element of the array will point to the corresponding string literal.

constexpr is safe and you can use the array elements as you would individual string literals via const char* pointer. In particular trying to modify these literals or the array via const_cast will have undefined behavior though.

const std::array<std::string, 3> also works, but will not be usable in constant expressions. constexpr is not allowed on this because of std:string.

CTAD can also be used to deduce this type though with the help of string literal operators:

#include<string>
using namespace std::string_literals;

//...

const std::array myOtherStrings = { "one"s, "two"s, "three"s };

or since C++20:

const auto myOtherStrings = std::to_array<std::string>({ "one", "two", "three" });
爱的十字路口 2025-01-28 12:21:42

我想指出的是,如果您想最初用一些cTAD不起作用的空指针填充数组,除非编译器知道指针与您的角色数据是相同的类型,那么

std::array a = { "value", nullptr, "stuff" };

不起作用,因为编译器不使用' t知道应与NULL指针相关联的哪种类型,但是

std::array b = { reinterpret_cast<const char*>(nullptr), "more" };

确实有效,因为编译器可以看到NULL指针与const char*值相关联。

I want to point out that if you want to initially populate your array with some null pointers that CTAD doesn't work unless the compiler knows that the pointer is the same type as your character data, so

std::array a = { "value", nullptr, "stuff" };

doesn't work because the compiler doesn't know what type the null pointer should be associated with, but

std::array b = { reinterpret_cast<const char*>(nullptr), "more" };

does work because the compiler can see the null pointer is associated with const char* values.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文