不同翻译单元中具有静态存储持续时间的依赖非局部常量浮点变量的常量初始化
Constant initialization of dependent non-local constant float variables w/ static storage duration in different translation units
我想知道当两个不同翻译单元中具有静态存储持续时间的两个常量非局部浮点变量之间存在依赖关系时,我是否可以依赖常量初始化 - 其中一个依赖于(初始化为 [值of]) 另一个,而对于后者,执行常量初始化。我正在寻找提供和解释标准相关部分的答案,尤其是 C++11 标准。
// Note: the non-use of constexpr is intended (C++03 compatibility)
// foo.h
struct Foo {
static const float kValue;
};
// foo.cpp
const float Foo::kValue = 1.5F;
// bar.h
struct Bar {
static const float kValue;
};
// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue; // Constant initialization?
// main.cpp
#include "bar.h"
#include <iostream>
int main() { std::cout << Bar::kValue; }
- 非局部(常量)变量
Bar::kValue
,静态存储时长,是通过常量初始化的方式初始化的吗? (依次回答是静态初始化还是动态初始化)
我自己的细节/调查
[basic.start.init]/1 表示 [强调 我的]:
Constant initializationis performed:
if each full-expression (including implicit conversions) that
appears in the initializer of a reference with static or thread
storage duration is a constant expression (5.19) and the reference is
bound to an lvalue designating an object with static storage duration
or to a temporary (see 12.2);
if an object with static or thread storage duration is initialized
by a constructor call, and if the initialization full-expression is a
constant initializer for the object;
if an object with static or thread storage duration is not
initialized by a constructor call and if either the object is
value-initialized or every full-expression that appears in its
initializer is a constant expression.
从最后一个项目符号中解释,如果 Foo::kValue
是常量表达式,则 Bar::kValue
通过常量初始化的方式进行初始化。我想我可以在 [expr.const] 中找到这是否属实的答案,但我被卡住了。
嗯...我不会相信那个代码,因为我会害怕static initialization order fiasco。 AFAIK,不同编译单元之间静态初始化的顺序是不确定的。这意味着即使测试也不会让我相信一切都会好起来。
在不深入细节的情况下,我无法在标准中找到任何东西来确保 foo.cpp
中的 Foo::kValue
在 bar.cpp
中的 Bar::kValue
之前被初始化。而如果顺序错了,Foo::kValue
中的值将只是不确定的。
const float
不满足成为 常量表达式的要求
(这个答案是基于,因为他没有自己做答案)
在下面:
// foo.h
struct Foo {
static const float kValue;
};
// foo.cpp
const float Foo::kValue = 1.5F;
Foo::kValue
确实是由常量表达式通过常量初始化初始化的,但是Foo::kValue
是本身不是常量表达式,因为它既不是整数、枚举、constexpr,也不是临时的。 [expr.const]/2 声明 [强调 我的]:
A
conditional-expression
is a core constant expression unless it involves one of the following as a potentially evaluated subexpression
([basic.def.odr]),
but subexpressions of logical AND
([expr.log.and]),
logical OR
([expr.log.or]),
and conditional
([expr.cond])
operations that are not evaluated are not considered [ Note: An
overloaded operator invokes a function. — end note ]:
...
(2.9): an lvalue-to-rvalue conversion ([conv.lval])
unless it is applied to
- a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized
with a constant expression, or
- a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an
object, or
- a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant
expression;
由于 sub-clauses 的 none for (2.9) 适用于此,因此 Foo::kValue
不是常量表达式。从 [basic.start.init]/2(如问题中较早的标准版本中引用的那样)可以看出,Bar::kValue
不是通过 常量初始化 初始化的,而是作为 动态初始化.
Variables with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) shall be zero-initialized ([dcl.init]) before any other initialization takes place [emphasis mine]:
Constant initialization is performed:
- ...
- if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that
appears in its initializer is a constant expression.
关于“static initialization order fiasco”的注释
请注意,此特定示例不会导致静态初始化顺序失败的风险,因为 Foo::kValue
被初始化为常量初始化,而 Bar::kValue
被初始化为动态初始化的一部分,前者保证在动态初始化开始前完成。
如果前者也作为动态初始化的一部分进行初始化,则两者的初始化相对于彼此(以及所有其他动态初始化)的顺序将不确定。
然而,永远不要依赖这个特定示例具有 well-defined 初始化顺序的事实,因为细微的更改会使这一事实无效:
// foo.h
struct Foo {
static const float kDummyValue;
static const float kValue;
};
// foo.cpp
const float Foo::kDummyValue = 1.5F; // Constant initialization
const float Foo::kValue = kDummyValue; // (!) Dynamic initialization
// bar.h
struct Bar {
static const float kValue;
};
// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue; // (!) Dynamic initialization
// main.cpp
#include "bar.h"
#include <iostream>
int main() { std::cout << Bar::kValue; }
正如在这个修饰符示例中,Foo::kValue
和 Bar::kValue
的初始化相对于彼此的顺序是不确定的,这意味着 Bar::kValue
可以被初始化(使用 "value" of Foo::kValue
) before Foo::kValue
is.
我想知道当两个不同翻译单元中具有静态存储持续时间的两个常量非局部浮点变量之间存在依赖关系时,我是否可以依赖常量初始化 - 其中一个依赖于(初始化为 [值of]) 另一个,而对于后者,执行常量初始化。我正在寻找提供和解释标准相关部分的答案,尤其是 C++11 标准。
// Note: the non-use of constexpr is intended (C++03 compatibility)
// foo.h
struct Foo {
static const float kValue;
};
// foo.cpp
const float Foo::kValue = 1.5F;
// bar.h
struct Bar {
static const float kValue;
};
// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue; // Constant initialization?
// main.cpp
#include "bar.h"
#include <iostream>
int main() { std::cout << Bar::kValue; }
- 非局部(常量)变量
Bar::kValue
,静态存储时长,是通过常量初始化的方式初始化的吗? (依次回答是静态初始化还是动态初始化)
我自己的细节/调查
[basic.start.init]/1 表示 [强调 我的]:
Constant initializationis performed:
if each full-expression (including implicit conversions) that appears in the initializer of a reference with static or thread storage duration is a constant expression (5.19) and the reference is bound to an lvalue designating an object with static storage duration or to a temporary (see 12.2);
if an object with static or thread storage duration is initialized by a constructor call, and if the initialization full-expression is a constant initializer for the object;
if an object with static or thread storage duration is not initialized by a constructor call and if either the object is value-initialized or every full-expression that appears in its initializer is a constant expression.
从最后一个项目符号中解释,如果 Foo::kValue
是常量表达式,则 Bar::kValue
通过常量初始化的方式进行初始化。我想我可以在 [expr.const] 中找到这是否属实的答案,但我被卡住了。
嗯...我不会相信那个代码,因为我会害怕static initialization order fiasco。 AFAIK,不同编译单元之间静态初始化的顺序是不确定的。这意味着即使测试也不会让我相信一切都会好起来。
在不深入细节的情况下,我无法在标准中找到任何东西来确保 foo.cpp
中的 Foo::kValue
在 bar.cpp
中的 Bar::kValue
之前被初始化。而如果顺序错了,Foo::kValue
中的值将只是不确定的。
const float
不满足成为 常量表达式的要求
(这个答案是基于
在下面:
// foo.h struct Foo { static const float kValue; }; // foo.cpp const float Foo::kValue = 1.5F;
Foo::kValue
确实是由常量表达式通过常量初始化初始化的,但是Foo::kValue
是本身不是常量表达式,因为它既不是整数、枚举、constexpr,也不是临时的。 [expr.const]/2 声明 [强调 我的]:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression ([basic.def.odr]), but subexpressions of logical AND ([expr.log.and]), logical OR ([expr.log.or]), and conditional ([expr.cond]) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function. — end note ]:
...
(2.9): an lvalue-to-rvalue conversion ([conv.lval]) unless it is applied to
- a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or
- a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or
- a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant expression;
由于 sub-clauses 的 none for (2.9) 适用于此,因此 Foo::kValue
不是常量表达式。从 [basic.start.init]/2(如问题中较早的标准版本中引用的那样)可以看出,Bar::kValue
不是通过 常量初始化 初始化的,而是作为 动态初始化.
Variables with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) shall be zero-initialized ([dcl.init]) before any other initialization takes place [emphasis mine]:
Constant initialization is performed:
- ...
- if an object with static or thread storage duration is not initialized by a constructor call and if every full-expression that appears in its initializer is a constant expression.
关于“static initialization order fiasco”的注释
请注意,此特定示例不会导致静态初始化顺序失败的风险,因为 Foo::kValue
被初始化为常量初始化,而 Bar::kValue
被初始化为动态初始化的一部分,前者保证在动态初始化开始前完成。
如果前者也作为动态初始化的一部分进行初始化,则两者的初始化相对于彼此(以及所有其他动态初始化)的顺序将不确定。
然而,永远不要依赖这个特定示例具有 well-defined 初始化顺序的事实,因为细微的更改会使这一事实无效:
// foo.h
struct Foo {
static const float kDummyValue;
static const float kValue;
};
// foo.cpp
const float Foo::kDummyValue = 1.5F; // Constant initialization
const float Foo::kValue = kDummyValue; // (!) Dynamic initialization
// bar.h
struct Bar {
static const float kValue;
};
// bar.cpp
#include "foo.h"
const float Bar::kValue = Foo::kValue; // (!) Dynamic initialization
// main.cpp
#include "bar.h"
#include <iostream>
int main() { std::cout << Bar::kValue; }
正如在这个修饰符示例中,Foo::kValue
和 Bar::kValue
的初始化相对于彼此的顺序是不确定的,这意味着 Bar::kValue
可以被初始化(使用 "value" of Foo::kValue
) before Foo::kValue
is.