【问题标题】:Do we have a static initialisation order fiasco while initialising variables inside the class在类内初始化变量时,我们是否有静态初始化顺序惨败
【发布时间】:2021-07-18 09:55:42
【问题描述】:

我正在尝试将我们的旧代码从 c++98 重构为 c++14。并且需要使用较旧的 gcc(c++98) 和较新的 gcc(c++14) 进行编译。这就是我们旧代码的样子(这完全是我们复杂代码的示例。这个类的静态常量在另一个类中使用)。

// BankAccount.h
namespace bank {
class BankAccount {
 public:
    static const unsigned int account_number = 123456789;
    static const double balance = 0.0;
    BankAccount();
    ....
};
}

// BankAccount.cpp
namespace bank {
    BankAccount::BankAccount() {}
    ....
}

看起来从 c++11 开始,只有 int 和 enums 只允许在类声明中初始化。

问题:通过像上面那样在类声明中初始化静态 const 变量,是否可以避免静态初始化顺序的失败?看起来像上面的代码,我们从来没有观察到这个静态初始化顺序问题,如果我移动静态双精度并浮动到 .cpp 文件,我应该担心这个问题吗?

【问题讨论】:

  • 关于account_numberbalance,您在哪里看到任何订单问题?这两个变量彼此无关。 AFAIK,类成员必须按照声明的顺序进行初始化,但在这种情况下是不管的。当一个静态变量依赖于另一个时(例如,第二个的构造函数取决于构造的第一个),情况会有所不同。
  • 顺便说一句。你确定account_numberbalancestatic 的合适人选吗?我不是。这将导致BankAccount 的每个实例将共享相同的成员account_numberbalance(具有相同的存储空间)。 (这就是 static 类变量成员在 C++ 中的含义。)如果是我的银行帐户,我会立即切换到另一家银行...
  • @Scheff 这完全是我们复杂代码的示例。一个类的静态常量用于另一个类。
  • @Scheff,我弄错了,它有一个 const 说明符。

标签: c++ c++14 c++98 static-initialization static-order-fiasco


【解决方案1】:

(更新:由于问题已更改为static const成员)

通过声明它们static const,整数变量是安全的。在现代 C++ 中,有no difference to declaring them as static constexpr。语言保证您不会遇到访问未初始化(或更确切地说是零初始化)数值的问题。

对于像 double 这样的浮点数,它不应该编译,除非你使用 constexpr。但是,这是 C++98 中没有的功能。没有constexpr,你会得到这样的编译错误:

// gcc
example.cpp:6:25: error: ‘constexpr’ needed for in-class initialization of static data member ‘const double bank::BankAccount::balance’ of non-integral type [-fpermissive]
    6 |     static const double balance = 0.0;
      |                         ^~~~~~~

或仅具有非标准功能:

// clang with -Wall -std=c++98
example.cpp:6:25: warning: in-class initializer for static data member of type 'const double' is a GNU extension [-Wgnu-static-float-init]
    static const double balance = 0.0;

(旧答案没有声明它们const,但让它们非常量)

你确定这个例子吗?我认为它不会编译(在 C++98 和现代 C++ 中)。除非您将初始化移出类定义,否则您将得到类似的结果:

// gcc
example.cpp:5:30: error: ISO C++ forbids in-class initialization of non-const static member ‘bank::BankAccount::account_number’
    5 |     static unsigned long int account_number = 123456789;
      |                              ^~~~~~~~~~~~~~
example.cpp:6:19: error: ‘constexpr’ needed for in-class initialization of static data member ‘double bank::BankAccount::balance’ of non-integral type [-fpermissive]
    6 |     static double balance = 0.0;
      |                   ^~~~~~~
// clang
example.cpp:5:30: error: non-const static data member must be initialized out of line
    static unsigned long int account_number = 123456789;
                             ^                ~~~~~~~~~
example.cpp:6:19: error: non-const static data member must be initialized out of line
    static double balance = 0.0;
                  ^         ~~~

如果你把它移走,那么你最终可能会以静态初始化顺序惨败告终。这些值将从零初始化开始,然后取决于链接器何时执行真正的初始化代码。

如果可以将变量声明为常量,那将是安全的。

【讨论】:

  • AFAIR,静态顺序失败对于不同模块的符号来说是一个问题,但如果 static 变量属于同一个翻译单元,则不是。 (它们应该按照它们在源代码中出现的顺序来定义。)Static Initialization Order Fiasco静态初始化顺序惨败是指具有静态存储时长的对象在不同的翻译单元中初始化(强调我的。)
  • @philipp,对不起,我刚刚将问题编辑为静态常量
  • @BhanuKiran 我更新了答案。简而言之,使用静态 const,您是安全的。
  • @PhilippClaßen 我是否应该担心初始化问题。如果我像这样将`static const double`折射到cpp文件中`const double BankAccount::balance = 1000.0`。 ?
  • @BhanuKiran 我的理解是它确实会引入问题,即它将取决于链接器决定的顺序。也许在您的示例中,您可以继续使用 GNU 扩展来支持旧系统,至少在 Linux 系统上实际上应该没问题(gcc.gnu.org/bugzilla/show_bug.cgi?id=11393)。对于现代编译器, constexpr 是要走的路。即使它很丑陋,我也可以考虑在转换期间使用一个宏,它可以解析为“const”(对于 GNU C++98)或“constexpr”(对于 C++11 及更高版本)。并希望 C++98 支持顺利进行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 2011-07-15
  • 1970-01-01
相关资源
最近更新 更多