У меня есть такой класс:
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
// more stuff
};
Обычно я хочу по умолчанию (ноль) инициализировать counts
массив, как показано.
Однако в выбранных местах, определенных профилированием, я хотел бы подавить инициализацию массива, потому что я знаю, что массив собирается быть перезаписан, но компилятор не достаточно умен, чтобы понять это.
Какой идиоматичный и эффективный способ создать такой «вторичный» конструктор с нулевым аргументом?
В настоящее время я использую класс тега, uninit_tag
который передается как фиктивный аргумент, например:
struct uninit_tag{};
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
event_counts(uninit_tag) {}
// more stuff
};
Затем я вызываю конструктор без инициализации, например, event_counts c(uninit_tag{});
когда я хочу подавить конструкцию.
Я открыт для решений, которые не включают создание фиктивного класса, или более эффективны в некотором роде, и т. Д.
источник
Ответы:
У вас уже есть правильное решение, и именно это я и хотел бы увидеть, просматривая ваш код. Это максимально эффективно, ясно и кратко.
источник
uninit_tag
аромат в каждом месте, где я хочу использовать эту идиому. Я надеялся, что уже есть что-то вроде такого типа индикатора, возможно, вstd::
.no_init
и использовал его во всех моих классах, где это необходимо.std::piecewise_construct_t
иstd::in_place_t
. Ни один из них не кажется разумным для использования здесь. Возможно, вы захотите определить глобальный объект вашего типа для использования всегда, поэтому вам не нужны скобки при каждом вызове конструктора. STL делает это сstd::piecewise_construct
дляstd::piecewise_construct_t
.Если тело конструктора пустое, оно может быть опущено или по умолчанию:
Тогда инициализация по умолчанию
event_counts counts;
останетсяcounts.counts
неинициализированной (здесь инициализация по умолчанию запрещена), а инициализацияevent_counts counts{};
значения будет инициализировать значениеcounts.counts
, эффективно заполняя его нулями.источник
int i;
мы принимаем, что оно не инициализируется нулями. Может быть, мы должны также принять, чтоevent_counts counts;
не инициализируется нулями и сделатьevent_counts counts{};
наш новый по умолчанию.Мне нравится ваше решение. Возможно, вы также рассмотрели вложенную структуру и статическую переменную. Например:
Со статической переменной неинициализированный конструктор может показаться более удобным:
Конечно, вы можете ввести макрос, чтобы сохранить типизацию и сделать его более систематическим
источник
Я думаю, что enum - лучший выбор, чем класс tag или bool. Вам не нужно передавать экземпляр структуры, и из вызывающей стороны ясно, какой вариант вы получаете.
Тогда создание экземпляров выглядит так:
Или, чтобы сделать его более похожим на подход класса тегов, используйте перечисление с одним значением вместо класса тега:
Тогда есть только два способа создать экземпляр:
источник
event_counts() : counts{} {}
counts
безоговорочно, а только когдаINIT
он установлен.bool
и другоеenum
прилично, но мы должны знать, что использование параметра вместо перегрузки имеет несколько другой семантический оттенок. В первом случае вы четко параметризуете объект, поэтому инициализированная / неинициализированная позиция становится его состоянием, тогда как передача объекта тега в ctor больше похожа на запрос класса к выполнению преобразования. Так что это не IMO вопрос синтаксического выбора.event_counts() : counts{} {}
.counts
инициализируется,std::fill
если не требуетсяNO_INIT
. Добавление конструктора по умолчанию, как вы предлагаете, приведет к двум различным способам инициализации по умолчанию, что не очень хорошая идея. Я добавил другой подход, который избегает использованияstd::fill
.Вы можете рассмотреть возможность двухфазной инициализации для вашего класса:
Приведенный выше конструктор не инициализирует массив нулем. Чтобы установить элементы массива на ноль, вы должны вызвать функцию-член
set_zero()
после построения.источник
std::function
в качестве аргумента конструктора нечто похожее наset_zero
аргумент по умолчанию. Затем вы должны передать лямбда-функцию, если вы хотите неинициализированный массив.Я бы сделал это так:
Компилятор будет достаточно умен, чтобы пропустить весь код, когда вы его используете
event_counts(false)
, и вы сможете точно сказать, что вы имеете в виду, вместо того, чтобы делать интерфейс вашего класса таким странным.источник
event_counts(false)
, что это значит? Вы понятия не имеете, не вернувшись назад и не посмотрев на название параметра. Лучше хотя бы использовать enum или, в этом случае, класс стража / тега, как показано в вопросе. Тогда вы получите более похожую декларацию,event_counts(no_init)
которая очевидна для всех по смыслу.event_counts(bool initCountr = true)
.boost::parameter
и вызывать ихevent_counts(initCounts = false)
для удобочитаемостиevent_counts(bool initCounts = true)
фактически является конструктором по умолчанию, поскольку каждый параметр имеет значение по умолчанию. Требуется только, чтобы он вызывался без указания аргументов,event_counts ec;
не заботится о том, что он не содержит параметров, или использует значения по умолчанию.Я бы использовал подкласс только для того, чтобы немного набрать:
Вы можете избавиться от фиктивного класса, изменив аргумент не инициализирующего конструктора на
bool
илиint
или что-то еще, так как он больше не должен быть мнемоническим.Вы также можете поменять местами наследование и определить
events_count_no_init
с помощью конструктора по умолчанию, как предложил Evg в своем ответе, и затем иметьevents_count
подкласс:источник
event_counts
, я хочу, чтобы он был типизированнымevent_count
, а неevent_count_uninitialized
так, поэтому я должен нарезать его прямо на конструкциюevent_counts c = event_counts_no_init{};
, которая, как мне кажется, устраняет большую часть экономии при наборе текста.event_count_uninitialized
объект - этоevent_count
объект. В этом весь смысл наследования, они не совсем разные типы.ecu
дляec
него работает, но не наоборот. Или, если вы используете шаблонные функции, они бывают разных типов и заканчиваются различными экземплярами, даже если поведение оказывается идентичным (а иногда и не будет, например, со статическими членами шаблона). Особенно при интенсивном использованииauto
это может определенно возникнуть и сбить с толку: я бы не хотел, чтобы способ инициализации объекта постоянно отражался в его типе.