在 class 中初始化变量时,我们是否有静态初始化顺序失败
Do we have a static initialisation order fiasco while initialising variables inside the class
我正在尝试将我们的旧代码从 c++98 重构为 c++14。并且需要同时使用较旧的 gcc(c++98) 和较新的 gcc(c++14) 进行编译。这就是我们旧代码的样子(这完全是我们复杂代码的示例。来自这个 class 的静态常量在另一个 class 中使用)。
// 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 开始,只允许在 class 声明中初始化整数和枚举。
问题:通过像上面那样在 class 声明中初始化静态常量变量,是否可以避免静态初始化顺序失败?看起来像上面的代码,我们从来没有观察到这个静态初始化顺序问题,如果我将静态双精度和浮点移动到 .cpp 文件,我应该担心这个问题吗?
(更新:由于问题已更改为 static const
成员)
通过声明它们static const
,对于整型变量是安全的。在现代 C++ 中,有 。语言保证您不会 运行 遇到访问未初始化(或零初始化)数值的问题。
对于像 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++ 中)。除非你将初始化移出 class 定义,否则你会得到类似的东西:
// 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;
^ ~~~
如果你把它移出去,那么你可能会以静态初始化顺序失败告终。这些值将从零初始化开始,然后取决于链接器何时执行真正的初始化代码。
不过,如果可以将变量声明为常量,那将是安全的。
我正在尝试将我们的旧代码从 c++98 重构为 c++14。并且需要同时使用较旧的 gcc(c++98) 和较新的 gcc(c++14) 进行编译。这就是我们旧代码的样子(这完全是我们复杂代码的示例。来自这个 class 的静态常量在另一个 class 中使用)。
// 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 开始,只允许在 class 声明中初始化整数和枚举。
问题:通过像上面那样在 class 声明中初始化静态常量变量,是否可以避免静态初始化顺序失败?看起来像上面的代码,我们从来没有观察到这个静态初始化顺序问题,如果我将静态双精度和浮点移动到 .cpp 文件,我应该担心这个问题吗?
(更新:由于问题已更改为 static const
成员)
通过声明它们static const
,对于整型变量是安全的。在现代 C++ 中,有
对于像 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++ 中)。除非你将初始化移出 class 定义,否则你会得到类似的东西:
// 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;
^ ~~~
如果你把它移出去,那么你可能会以静态初始化顺序失败告终。这些值将从零初始化开始,然后取决于链接器何时执行真正的初始化代码。
不过,如果可以将变量声明为常量,那将是安全的。