Почему sizeof (std:: variant) имеет тот же размер, что и структура с теми же членами?
Шаблон класса std::variant
представляет объединение с безопасным типом. Экземпляр std::variant
в любой момент времени либо содержит значение одного из его альтернативных типов, либо не содержит значения.
sizeof(std::variant<float, int32_t, double>) == 16
Но если это союз, почему он занимает так много места?
struct T1 {
float a;
int32_t b;
double c;
};
struct T2 {
union {
float a;
int32_t b;
double c;
};
};
Вариант имеет тот же размер, что и структура
sizeof(T1) == 16
sizeof(T2) == 8
Я ожидаю, что размер объединения плюс 4 байта для хранения, какой тип активен.
Ответы
Ответ 1
Здесь тип с наибольшим выравниванием в variant
является double
, с выравниванием 8. Это означает, что весь variant
должен иметь выравнивание 8. Это также означает, что его размер должен быть кратным 8, гарантируя, что в массиве каждый variant
будет иметь double
выравнивание по 8.
Но вариант должен хранить больше, чем просто самый большой тип: он также должен хранить идентификатор или тег, чтобы знать, какой тип в данный момент создается. Таким образом, даже если для этого идентификатора используется только 1 байт, структура в целом дополняется до 16, так что она кратна 8 размеру.
Более правильное сравнение будет:
struct
{
union
{
float a;
int32_t b;
double c;
};
int identifier;
};
Ответ 2
Основной ответ: для выравнивания. Так как один из ваших типов является двойным, а удваивает 8 байтов с 8-байтовыми требованиями к выравниванию, это означает, что вариант также имеет требование выравнивания по 8 байт. Таким образом, он может быть только кратным 8 байтам. Как вы сами отметили, минимальный размер - это самый большой тип + что-то дополнительное, чтобы указать, какой член активен. Это означает, что он не может вписаться в 8 байтов, но тогда требования к выравниванию вынуждают его полностью до 16 байтов.
Вот почему он не может быть 12 байтов, как вы думаете. Между прочим, нет также правила, согласно которому хранилище активного типа должно быть 4 байта. На самом деле, довольно ясно, что в подавляющем большинстве случаев вы можете обойтись одним байтом, который может различать 255 типов, плюс пустое состояние. Мы можем проверить это:
struct null {};
std::cerr << sizeof(std::variant<bool, null>);
Я только что проверил это на coliru, и он напечатал "2". В этом случае 1 байт для самого большого типа (bool) и 1 байт, чтобы определить, какой тип активен.
Ответ 3
В то время как варианты логически являются типами безопасных соединений, не гарантируется, что их размеры будут равны размерам сырых объединений с одинаковыми членами.
Понятно, что, поскольку вариант (в отличие от raw union) должен хранить информацию о текущем активном типе, он должен быть больше. Фактический размер варианта будет зависеть от архитектуры (дополнения) и реализации, поскольку Standard не налагает ограничений на размер.
Ответ 4
Как ни странно, вы были обмануты совпадением. Возврат следующего: 16:
sizeof(std::variant<float, int32_t, double, int64_t>)
И это тоже:
sizeof(std::variant<float, int32_t, double, int64_t, double>)
Таким образом, в основном есть внутренняя переменная в std::variant
, размер которой равен 8 байтам (или меньше, но соответствует 8 байтам). Это, в дополнение к вашему союзу, составляет 16.