c++:初始化constexpr lambda为constexpr/非constexpr变量



我正在阅读" c++ 17完整指南"Nicolai M. Josuttis的书,不能理解下面的例子

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在运行时被强制初始化。

这些变量没有在编译时可用的值。编译器很可能在运行时初始化它,即使不是强制的。这在运行时是有代价的,由于静态初始化顺序的失败,您可以在初始化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强制值为const。

constinit int value = 9;

现在编译器被迫使用常量初始化来初始化该值,而且该变量在运行时是可变的。这有效地解决了初始化顺序的惨败,因为您可以恒定地初始化变量。

最新更新