簡體   English   中英

在不同的翻譯單元中對帶有靜態存儲持續時間的相關非局部常量浮點變量進行常量初始化

[英]Constant initialization of dependent non-local constant float variables w/ static storage duration in different translation units

我想知道當兩個不同轉換單元中具有靜態存儲持續時間的兩個常量非局部浮點變量之間存在依賴關系時,我是否可以依賴於常量初始化-其中一個依賴於另一個(初始化為[值]) ,對於后者,執行常量初始化。 我正在尋找提供和解釋標准特別是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狀態[ 強調我的]:

進行常量初始化:

  • 如果出現在具有靜態或線程存儲持續時間的引用的初始化程序中出現的每個完整表達式(包括隱式轉換)是一個常量表達式(5.19)並且該引用綁定到一個指定具有靜態存儲持續時間的對象的左值或一個臨時對象(請參閱12.2);

  • 如果具有靜態或線程存儲持續時間的對象是通過構造函數調用初始化的,並且初始化full-expression是該對象的常量初始化器;

  • 如果具有靜態或線程存儲持續時間的對象未通過構造函數調用初始化,並且該對象是值初始化的,或者其初始化程序中出現的每個完整表達式都是一個常量表達式

從最后的項目符號解釋,如果Foo::kValue是常量表達式,則Bar::kValue將通過常量初始化進行初始化。 我懷疑我可以在[expr.const]中找到關於這是否正確的答案,但是在這里我被困住了。

嗯...我不信任該代碼,因為我擔心靜態初始化順序會失敗 AFAIK,不同編譯單元之間的靜態初始化順序不確定。 那意味着即使測試也不會使我確信一切都會好起來的。

如果不輸入細節,我在標准中找不到任何可以確保Foo::kValue中的Foo::kValuefoo.cpp Bar::kValue之前初始化的bar.cpp 如果順序錯誤, Foo::kValue的值將只是不確定的。

const float不滿足成為常量表達式的要求

(此答案基於@Oktalist的評論 ,因為他不願自己回答)。

在下面的:

 // foo.h struct Foo { static const float kValue; }; // foo.cpp const float Foo::kValue = 1.5F; 

Foo::kValue實際上是通過常量表達式通過常量初始化進行初始化的 ,但是Foo::kValue本身不是常量表達式,因為它既不是整數,枚舉,constexpr也不是臨時的。 [expr.const] / 2狀態[我的重點 ]:

條件表達式 核心常量表達式,除非它涉及以下之一作為可能評估的子表達式[basic.def.odr] ),但是邏輯AND( [expr.log.and] ),邏輯OR( [ expr.log.or] )和條件( [expr.cond] )未評價的操作不被認為是[注:一個重載操作調用的函數。 —尾注]:

...

(2.9)從左 值到右值的轉換[conv.lval] ), 除非將其應用於

  • 整數或枚舉類型的glvalue,該值引用具有先前初始化,使用常量表達式初始化的非易失性const對象,或者
  • 文字類型的glvalue,它引用用constexpr定義的非易失性對象,或引用該對象的子對象,或
  • 以常量表達式初始化的字面量類型的glvalue,它的生存期尚未結束的非易失性臨時對象;

由於(2.9)的所有子條款均不適用,因此Foo::kValue不是常量表達式。 [basic.start.init] / 2 (在問題的早期標准版本中引用)中可以得出, Bar::kValue不是通過常量初始化來初始化的 ,而是作為動態初始化的一部分進行初始化的

具有靜態存儲持續時間( [basic.stc.static] )或線程存儲持續時間( [basic.stc.thread] )的變量應在進行任何其他初始化之前進行零初始化( [dcl.init] )[ 強調我的]:

進行常量初始化

  • ...
  • 如果具有靜態或線程存儲持續時間的對象未通過構造函數調用初始化,並且其初始化程序中出現的每個完整表達式都是一個常量表達式

關於“ 靜態初始化順序慘敗 ”的注釋

請注意,此特定示例不會帶來靜態初始化順序失敗的風險,因為Foo::kValue被初始化為常量初始化的方式,而Bar::kValue被初始化為動態初始化的一部分,並且保證前者可以完成在動態初始化開始之前。

如果前者也將作為動態初始化的一部分進行初始化,則兩者的初始化將相對於彼此不確定地排序(以及所有其他動態初始化)。

但是,請不要依賴於此特定示例具有明確定義的初始化順序的事實,因為細微的更改會使該事實無效:

// 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::kValueBar::kValue的初始化相對於彼此不確定地順序,這意味着Bar::kValue可以在Foo::kValue之前初始化(使用Foo::kValue的“值”) Foo::kValue是。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM