c++ - C++:将 constexpr lambda 初始化为 constexpr/非 constexpr 变量
问题描述
我正在阅读 Nicolai M. Josuttis 的“C++ 17 The Complete Guide”一书,但无法理解以下示例
auto squared1 = [](auto val) constexpr { // example 1. compile-time lambda calls
return val * val;
};
以及对它的声明
如果(仅)lambda
constexpr
可以在编译时使用,但squared1
可能在运行时初始化,这意味着如果静态初始化顺序很重要(例如,导致静态初始化顺序失败),可能会出现一些问题。
作者建议考虑以下解决方案
constexpr auto squared = [](auto val) constexpr {
return val * val;
};
这意味着它将以某种方式避免先前的问题。
我不理解第一个示例关于初始化顺序的问题,因此无法理解作者的解决方案如何改进它。您能否解释一下并举例说明第一个示例的缺点?
解决方案
该语句auto square1 =
初始化一个全局变量,甚至不是常量。这是一个运行时变量,您可以对其进行变异、分配以及可能的其他内容。
你可以很好地初始化这样的变量:
auto returnMeALambda() {
int capture = rand() % 2;
return [capture](auto val) {
return val * val + capture;
}
}
auto square1 = returnMeALambda();
可以看到, 的代码returnMeALambda
严格来说是运行时的,所以square1
是在运行时强制初始化的。
这些变量没有在编译时可用的值。编译器很可能在运行时初始化它,即使不是强制的。这在运行时有成本,并且使用静态初始化顺序 fiasco,您可以在初始化之前从技术上使用 lambda,甚至在初始化之前使用另一个全局:
extern int baseval;
auto returnMeALambda() {
int capture = baseval + rand() % 2;
return [capture](auto val) {
return val * val + capture;
}
}
auto square1 = returnMeALambda();
int baseval = square1(2);
这段代码总是以未定义的行为结束,因为它总是使用未初始化的变量,无论初始化顺序如何。
作者提出的解决方案是将变量初始化为constexpr
. 在这种情况下,这会做三件事:
- 使变量
const
. 你不能在运行时改变它的值。 - 现在强制进行常量初始化。的值
square1
被编码在可执行的二进制代码中。 - 使变量值在编译时可用。您现在可以在 constexpr 上下文中使用该变量。
第二点是作者寻求作为解决方案的属性。保证该变量不会在运行时初始化,因为该值在编译时可用并保证编译器的持续初始化。
请注意,在 C++20 中,您可以自行应用第二点,而无需使用constinit
.
constinit int value = 9;
现在编译器被迫使用常量初始化来初始化值,而且变量在运行时是可变的。鉴于您可以不断地初始化变量,这有效地解决了初始化顺序的失败。
推荐阅读
- reactjs - 根据另一个字段值响应更改初始状态
- docker - 如何修复“容器运行时已关闭,PLEG 不健康”
- sql - 在 Access 2016 中返回数千条记录但在 SQL Server 中仅返回一百条记录的相同查询
- doctrine-orm - Doctrine DQL 在查询生成器上引发致命错误
- docker - 添加 dockerfile 所在目录的内容的正确方法是什么?
- python-3.x - API xStation xtb log in troubles
- javascript - 将我的 API 令牌存储在本地存储中是将令牌用双引号引起来
- android - Read android's versionName from build.gradle in Jenkins pipeline
- google-cloud-platform - 触发云构建需要什么权限?
- reactjs - react-redux v6 需要一个 v3.*.* 版本的 react-redux-firebase