Произошло ли фиаско с порядком статической инициализации при инициализации переменных внутри класса?

Я пытаюсь реорганизовать наш старый код с С ++ 98 на С ++ 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() {}
    ....
}

Похоже, что из С ++ 11 только целые числа и перечисления можно инициализировать только с помощью объявления класса.

Вопрос: Инициализируя статические константные переменные внутри объявления класса, как указано выше, избегает ли он фиаско с порядком статической инициализации? Похоже, что в приведенном выше коде мы никогда не наблюдали эту проблему статического порядка инициализации. Следует ли мне беспокоиться о проблеме, если я переместил статический двойной и плавающий в файл .cpp?


person BhanuKiran    schedule 24.04.2021    source источник
comment
Что касается account_number и balance, где вы видите проблемы с заказом? Эти две переменные не связаны друг с другом. AFAIK, члены класса должны быть инициализированы в объявленном порядке, но в этом случае это независимо. Было бы иначе, если бы одна статическая переменная зависела от другой (например, конструктор 2-го в зависимости от построенного 1-го).   -  person Scheff's Cat    schedule 24.04.2021
comment
Кстати. Вы уверены, что account_number и balance подходящие кандидаты на то, чтобы быть static? Я скорее не буду. Это приведет к тому, что каждый экземпляр BankAccount будет иметь одни и те же члены account_number и balance (с одинаковым хранилищем). (Это то, что означает член переменной класса static в C ++.) Если бы это был мой банковский счет, я бы немедленно переключился в другой банк ...   -  person Scheff's Cat    schedule 24.04.2021
comment
@Scheff Это полностью составленный пример нашего сложного кода. Статические константы из одного класса используются в другом классе.   -  person BhanuKiran    schedule 24.04.2021
comment
@Scheff, я ошибся, у него есть спецификатор const.   -  person BhanuKiran    schedule 24.04.2021


Ответы (1)


(Обновление: поскольку вопрос был изменен на static const участников)

Объявляя их static const, это безопасно для целочисленных переменных. В современном C ++ нет разницы в объявлении их как 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;
                  ^         ~~~

Если вы его переместите, то можете потерпеть фиаско с порядком статической инициализации. Значения будут начинаться с инициализации нулями, а затем в зависимости от компоновщика будет выполняться реальный код инициализации.

Было бы безопасно, если бы переменную можно было объявить как константу.

person Philipp Claßen    schedule 24.04.2021
comment
AFAIR, фиаско статического порядка является проблемой для символов разных модулей, но не, если переменные static принадлежат одной и той же единице трансляции. (Они должны быть определены в том порядке, в котором они появляются в исходном коде.) Статический порядок инициализации Fiasco: Фиаско с порядком статической инициализации связано с неоднозначностью порядка, в котором объекты со статической продолжительностью хранения в различных единицах трансляции инициализируются в (подчеркну мое.) - person Scheff's Cat; 24.04.2021
comment
@philipp, извините, я только что отредактировал вопрос на static const - person BhanuKiran; 24.04.2021
comment
@BhanuKiran Я обновил ответ. Короче говоря, с static const вы в безопасности. - person Philipp Claßen; 24.04.2021
comment
@ PhilippClaßen Следует ли мне беспокоиться о проблеме инициализации. если я перенаправляю `static const double` в файл cpp, например,` const double BankAccount :: balance = 1000.0`. ? - person BhanuKiran; 24.04.2021
comment
@BhanuKiran Насколько я понимаю, это действительно вызовет проблему, то есть тогда это будет зависеть от порядка, который выберет компоновщик. Возможно, в вашем примере вы можете продолжать использовать расширение GNU для поддержки старых систем, по крайней мере, в системах Linux это должно быть нормально на практике (gcc.gnu.org/bugzilla/show_bug.cgi?id=11393). Для современных компиляторов constexpr - лучший вариант. Даже если это некрасиво, я мог бы рассмотреть возможность использования макроса во время перехода, который разрешается либо в const (для GNU C ++ 98), либо в constexpr (для C ++ 11 и выше). И надеюсь, что поддержка C ++ 98 будет успешной. - person Philipp Claßen; 24.04.2021