Раздел 9.4.2, статические элементы данных, С ++ стандартных состояний:
Если член static
данные из const
интеграла или const
перечисления типа, его заявление в определении класса можно указать сопзЬ -initializer, который должен быть интегральным постоянным выражением.
Следовательно, значение статического члена данных может быть включено «внутри класса» (по которому я предполагаю, что вы имеете в виду в объявлении класса). Однако тип элемента статических данных должен быть интегралом const
или const
. Причина, по которой значения статических элементов данных других типов не могут быть указаны в объявлении класса, заключается в том, что, вероятно, требуется нетривиальная инициализация (т. Е. Должен выполняться конструктор).
Представьте себе, если следующие законны:
// my_class.hpp
#include <string>
class my_class
{
public:
static std::string str = "static std::string";
//...
Каждого объектный файл, соответствующий файлы CPP, которые включают этот заголовок не только иметь копию пространства для хранения my_class::str
(состоящих из sizeof(std::string)
байт), а также «секция ctor», которая вызывает конструктор std::string
, берущий C-строку. Каждая копия места хранения для my_class::str
будет идентифицирована с помощью общей метки, поэтому линкер может теоретически объединить все копии пространства памяти в один. Тем не менее, компоновщик не сможет изолировать все копии кода конструктора внутри разделов ctor объектных файлов. Было бы как спрашивать компоновщик, чтобы удалить весь код инициализации str
в компиляции следующего:
std::map<std::string, std::string> map;
std::vector<int> vec;
std::string str = "test";
int c = 99;
my_class mc;
std::string str2 = "test2";
EDIT Поучительно посмотреть на выходе сборщика г ++ для следующего кода:
// SO4547660.cpp
#include <string>
class my_class
{
public:
static std::string str;
};
std::string my_class::str = "static std::string";
код сборки можно получить, выполнив:
g++ -S SO4547660.cpp
Просматривая SO4547660.s
файл, который генерирует g ++, вы можете видеть, что для такого небольшого исходного файла существует много кода.
__ZN8my_class3strE
- это метка пространства для хранения my_class::str
.Существует также источник сборки функции __static_initialization_and_destruction_0(int, int)
, который имеет метку __Z41__static_initialization_and_destruction_0ii
. Эта функция специально для g ++, но просто знайте, что g ++ будет удостовериться, что она вызывается до того, как будет запущен любой код без инициализатора. Обратите внимание, что реализация этой функции вызывает __ZNSsC1EPKcRKSaIcE
. Это искаженный символ для std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)
.
Возвращаясь к гипотетическому примеру выше, и используя эти данные, каждый объектный файл, соответствующий файл CPP, который включает my_class.hpp
будет иметь метку __ZN8my_class3strE
для sizeof(std::string)
байт, а также код сборки для вызова __ZNSsC1EPKcRKSaIcE
в его реализации __static_initialization_and_destruction_0(int, int)
функция. Компоновщик может легко объединить все вхождения __ZN8my_class3strE
, но он не может изолировать код, который вызывает __ZNSsC1EPKcRKSaIcE
в реализации объектного файла __static_initialization_and_destruction_0(int, int)
.
Правило с одним определением: «Никакая единица перевода не должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона». Если ваш первый пример «Gizmo» был законным, я не думаю, что он нарушил бы правило определения, потому что каждая единица перевода * имела бы одно определение «Gizmo :: name». –
@ Daniel Trebbien: Это не весь ODR. Это всего лишь 3.2/1 - первый грубый «слой» ODR (чтобы позаботиться о самых очевидных нарушениях). Полный ODR имеет более подробный набор требований для каждого типа сущности. Для объектов внешней связи (а также функций внешней связи) ODR дополнительно ограничено в 3.2/3 одним и единственным определением * для всей программы *. – AnT
@ Daniel Trebbien: Причина, по которой требование 3.2/1 было отделено от остальных, заключается в том, что нарушение 3.2/1 требует диагностики от компилятора, тогда как для нарушений 3.2/3 диагностика не требуется. – AnT