У меня есть структура со многими членами одного типа, как это
struct VariablePointers {
VariablePtr active;
VariablePtr wasactive;
VariablePtr filename;
};
Проблема в том, что если я забуду инициализировать один из членов структуры (например wasactive
), например:
VariablePointers{activePtr, filename}
Компилятор не будет жаловаться на это, но у меня будет один объект, который частично инициализирован. Как я могу предотвратить подобные ошибки? Я мог бы добавить конструктор, но он дважды продублировал бы список переменных, так что мне пришлось бы напечатать все это трижды!
Пожалуйста, также добавьте ответы C ++ 11 , если есть решение для C ++ 11 (в настоящее время я ограничен этой версией). Более поздние языковые стандарты также приветствуются!
c++
aggregate-initialization
Йоханнес Шауб - Литб
источник
источник
-Wmissing-field-initializers
флаг компиляции.Ответы:
Вот трюк, который вызывает ошибку компоновщика, если отсутствует необходимый инициализатор:
Применение:
Результат:
Предостережения:
Foo
вообще не позволяет быть агрегатом.источник
Foo
объявлен, даже если вы никогда не вызываете оператора.Для clang и gcc вы можете скомпилировать,
-Werror=missing-field-initializers
что превращает предупреждение об отсутствующих инициализаторах поля в ошибку. godboltРедактировать: Для MSVC, кажется, нет предупреждения даже на уровне
/Wall
, поэтому я не думаю, что с помощью этого компилятора можно предупредить об отсутствующих инициализаторах. godboltисточник
Полагаю, это не элегантное и удобное решение, но должно работать и с C ++ 11 и выдавать ошибку во время компиляции (не во время компоновки).
Идея состоит в том, чтобы добавить в вашу структуру дополнительный член, в последней позиции, типа без инициализации по умолчанию (и который не может инициализироваться значением типа
VariablePtr
(или каким-либо другим типом предыдущих значений)Примером
Таким образом, вы вынуждены добавить все элементы в ваш список инициализации агрегата, включая значение для явной инициализации последнего значения (
sentinel
в примере это целое число ) или вы получаете ошибку «вызов удаленного конструктора« bar »».Так
компилировать и
не делает.
К сожалению также
не компилируется
-- РЕДАКТИРОВАТЬ --
Как указал MSalters (спасибо), в моем исходном примере есть дефект (еще один дефект):
bar
значение может быть инициализированоchar
значением (которое можно преобразовать вint
), поэтому работает следующая инициализацияи это может быть очень запутанным.
Чтобы избежать этой проблемы, я добавил следующий удаленный конструктор шаблона
поэтому предыдущее
f4
объявление дает ошибку компиляции, потому чтоd
значение перехватывается удаляемым конструктором шаблонаисточник
foo f;
не может скомпилироваться, но, возможно, это скорее особенность, чем недостаток этого трюка. Приму, если нет лучшего предложения, чем это.enum
, и назватьinit_list_end
(простоlist_end
) значение этогоenum
; но удобочитаемость добавляет много машинописи, поэтому, учитывая, что дополнительное значение является слабым местом этого ответа, я не знаю, хорошая ли это идея.constexpr static int eol = 0;
в шапкеbar
.test{a, b, c, eol}
кажется довольно читабельным для меня.bar::eol
; это почти как передатьenum
значение; но я не думаю, что это важно: суть ответа - «добавьте в свою структуру дополнительный член, в последней позиции, типа без инициализации по умолчанию»; этаbar
часть - просто тривиальный пример, показывающий, что решение работает; точный «тип без инициализации по умолчанию» должен зависеть от обстоятельств (ИМХО).Для CppCoreCheck есть правило для точной проверки, если все элементы были инициализированы, и это можно превратить из предупреждения в ошибку - это обычно для всей программы, конечно.
Обновить:
Правило, которое вы хотите проверить, является частью безопасности типов
Type.6
:источник
Самый простой способ - не давать типу членов конструктор без аргументов:
Другой вариант: если ваши члены являются постоянными, вы должны инициализировать их всех:
Если вы можете жить с одним фиктивным константой и участником, вы можете объединить это с идеей @ max66 о страже.
Из cppreference https://en.cppreference.com/w/cpp/language/aggregate_initialization
Другой вариант - взять сторожевую идею max66 и добавить синтаксический сахар для удобства чтения.
источник
A
неподвижным и изменяет семантику копирования (A
так сказать, больше не является совокупностью значений) :(1,2,3
объекты являются автоматически локальными в автоматическом хранилище, которые выходят из области видимости после завершения функции. И это делает размер (A) 24 вместо 3 в системе с 64-битными указателями (например, x86-64).