Является ли кортеж гарантированным сжать пустые элементы?
Реализации std::tuple
в libstdС++ и libС++ используют (я полагаю) пустую оптимизацию базового класса для сжатия пустых элементов:
struct empty {};
static_assert(sizeof(std::tuple<int, empty>) == sizeof(int), ""); // (1)
Мой вопрос в том, просто ли это поведение, установленное стандартом? То есть, могу ли я полагаться (1) всегда быть истинным для стандартно-совместимой реализации?
Ответы
Ответ 1
Нет, это не гарантировано. С++ 11 § 20.4 (глава о std::tuple
) вообще не упоминает размер типа. Таким образом, нет никаких гарантий относительно того, как организованы члены в кортеже. Любая оптимизация с пустым основанием и аналогичные эффекты являются исключительно проблемой качества реализации.
Обратите внимание, что это означает, что даже нет гарантии, что std::tuple<int, char>
будет сохранен в памяти как int
, за которым следует char
, а не наоборот. Макет объекта std::tuple
полностью не указан.
Ответ 2
Нет. Это не. На самом деле есть места, когда вы вообще не можете пустую базовую класс.
Стандарт С++ указывает, что два разных объекта должны иметь разные адреса.
рассмотрим следующее: std::tuple<char, empty, empty>
. Независимо от того, как вы выглядите (состав, наследование), у вас есть два объекта empty
, которые должны иметь разные адреса. Это увеличит размер кортежа хотя бы на 1.
Проблема может возникнуть из-за косвенного наследования:
struct derived: empty
{
char i;
};
std::tuple<derived, empty>
Здесь у нас есть два объекта empty
внутри одного класса, и у них также должны быть разные адреса, поэтому вы не можете оптимизировать второй член empty
из кортежа.
EDIT: обнаружено неожиданное поведение с пустым основанием и aligned_storage
, вытекающее из агрессивного EBO: http://coliru.stacked-crooked.com/a/f45de2f889151ea3