为什么创建 static const std::string 会导致异常?

Why can creating a static const std::string cause an exception?

我有字符串常量,用于我在我的应用程序的多个位置使用的字符串:

namespace Common{
    static const std::string mystring = "IamAwesum";
}

在发布有关其他问题 () 的问题时,另一位用户发表了以下评论:

be aware that your static string are global in this case. So they are could create an exception at anytime and can't be catch. I advise you to use function who return a reference of your string. std::string const &mystring { static std::string const mystring = "IamAwesum"; return mystring} by this way your object is only construct when needed

有人可以解释为什么以我上面的方式使用静态常量字符串会有抛出异常的风险吗?

N4140 § 3.6.2 [basic.start.init]/ 4

It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main.

N4140 § N4140 15.3 [except.handle]/ 13

Exceptions thrown in destructors of objects with static storage duration or in constructors of namespace-scope objects with static storage duration are not caught by a function-try-block on main().

您根本无法捕获字符串构造函数生成的异常 - 例如,std::bad_alloc.

(意见)话虽这么说,对于这么小的字符串,我觉得这种考虑是偏执的。

pdf 文档主要是指对象构造函数的异常和静态或动态链接库的初始化顺序失败。

我在您的代码中看到的唯一危险是 std::string 的构造函数在被调用时会抛出异常。

如果你真的想安全起见,你可以使用 static const char* mystring 代替,它不会调用 C++ ctor。

还有一个问题是代码位于共享库中,然后需要将其放置在进程的地址 space 中。 如果你不使用复杂的 ctors(可以抛出的 ctors),我不认为这是一个主要问题。

唯一的 "issue"——如果你可以这样称呼它——我在你的代码中看到的是,你通过不必要地将已经常量的数据复制到动态分配的缓冲区(这是形式上不变,但实际上不是)。这使用的物理内存是必要的两倍,并进行了不必要的复制。

重要吗?几乎可以肯定,没有。即使在 "rather limited memory" 系统上,无论是从执行时间的角度还是从内存消耗的角度来看,现在都很难注意到这一点。

至于异常,技术上当然std::string必须使的分配失败,因此构造函数 可以 抛出,而你将无法捕捉到它。但请现实一点。
这几乎可以保证不会发生,但即使它发生了......如果在程序启动时像为几个字符串分配内存这样微不足道的事情失败了,那么你就会遇到一个非常非常严重的问题,其规模完全不同!
此外,正如对上面另一个答案的评论中指出的那样:假设确实发生了这种情况,您将如何处理?该程序完全无法 运行,因此您可以想像地终止该程序。

现在,C++17 已经不远了,而且 string_view 已经在几个主流编译器的 std::experimental 中可用,你可以尝试另一件事:使用正确的事情.

A string_view 将与 string 相反,不分配非常量内存,将常量数据复制到其中,然后 假装 它是常量.相反,它将管理一个直接指向常量数据的指针,仅此而已。
这样,您的常量是真正的(不仅仅是形式上的)常量,没有分配,没有异常的可能性,也没有双重内存使用。在大多数情况下,它看起来和闻起来仍然像 string。唯一值得注意的区别是 string_view 不保证 null 终止(但它指向的字符常量确实如此,所以这是无关紧要的),而且它 确实 常量,不可修改...这正是您想要的。