в С++ 11, можете ли вы всегда (безопасно) заменить инициализацию memset() пустым инициализатором?

Я часто сталкиваюсь со структурами POD в коде, которые вручную инициализируются нулями с помощью memset, например так:

struct foo;
memset(&foo, 0, sizeof(foo));

Я проверил стандарт C++11, и он говорит: "Объект, инициализатором которого является пустой набор круглых скобок, то есть (), должен быть инициализирован значением". За этим следует:< /strong> "Инициализация значением [структуры pod] типа T означает... объект инициализируется нулями."

Итак... значит ли это, что вы можете всегда безопасно сократить приведенный выше код до следующего:

struct foo{};

И иметь гарантированно инициализированную структуру, как если бы вы вызвали memset(&foo, 0, ...)?


Если да, то, вообще говоря, можете ли вы безопасно инициализировать что-либо с помощью пустых инициализаторов, например:

SomeUnknownType foo{};  // will 'foo' be completely "set" to known values?

Я знаю, что это не всегда было возможно в C++03 (до унифицированного синтаксиса инициализации), но это возможно сейчас в С++ 11 для любого типа?


person MikeTusar    schedule 17.08.2013    source источник
comment
проверьте эту тему: stackoverflow.com/questions/620137/ Общий ответ - да.   -  person CS Pei    schedule 17.08.2013
comment
Имейте в виду, что MSVC никогда должным образом не поддерживала инициализацию значений, и в некоторых контекстах произойдет сбой. Похоже, Microsoft не заинтересована в исправлении ошибки.   -  person Casey    schedule 17.08.2013
comment
@Casey: Это довольно серьезная ошибка, интересно, исправлена ​​ли она в последней версии VS. К счастью, я использую gcc и clang (которые, кажется, заботятся о соответствии стандартам), и мой код не должен быть переносимым на другие компиляторы или даже на более старые версии gcc или clang.   -  person MikeTusar    schedule 17.08.2013
comment
См. также: stackoverflow.com/a/38103250/3223828   -  person Stuart Gillibrand    schedule 29.06.2016


Ответы (2)


Вы, безусловно, можете инициализировать любой стандартный тип макета, используя пустые круглые скобки, и инициализировать его нулем. Как только вы добавите конструкторы в картину, это не обязательно так, к сожалению:

struct foo {
    int f;
};
struct bar {
    int b;
    bar() {}
};

foo f{};
bar b{};

В то время как f.f инициализируется нулем, b.b не инициализируется. Однако вы ничего не можете сделать с bar, потому что вы тоже не можете memset(). Это необходимо исправить.

person Dietmar Kühl    schedule 17.08.2013
comment
Итак, подведем итог: везде, где memset() можно безопасно использовать для инициализации типа, может ли пустой инициализатор (скобки/скобки) добиться нулевой инициализации памяти... правильно? - person etherice; 17.08.2013
comment
@etherice: Правильно. memset() можно использовать только на POD, а POD всегда инициализируются нулем. Тем не менее, это относится ко всем участникам. Я не думаю, что пустые скобки/скобки дают какую-либо гарантию заполнения. Это не должно иметь значения, но иногда имеет значение. - person Dietmar Kühl; 17.08.2013
comment
Они сделали {} агрегатной инициализацией вместо инициализации значения для агрегатной структуры. Нулевая инициализация и, следовательно, инициализация значений агрегатов гарантирует, что заполнение равно нулю. Но я не верю, что агрегатная инициализация работает. - person Johannes Schaub - litb; 17.08.2013

Memset инициализирует его значением 0, но пустая скобка будет инициализировать значение вашего объекта (есть разница).

Цитата из стандарта:

Инициализация значения объекта типа T означает:

  • если T является типом класса с объявленным пользователем конструктором, то вызывается конструктор по умолчанию для T (и инициализация является неправильной, если T не имеет доступного конструктора по умолчанию);

Кроме того, выполнение struct foo{}; безопасно и предотвращает сброс указателя виртуальной таблицы.

Но делать memset(&foo, 0, sizeof(foo)); в случае виртуальных функций действительно очень плохо, так как он сбрасывает указатель на виртуальную таблицу.

person Saksham    schedule 17.08.2013
comment
Если в foo есть функция virtual, использование memset() для объекта приводит к неопределенному поведению. Вы можете использовать только memset() определенные типы. Раньше они назывались POD (обычные старые данные), но я думаю, что в C++11 они называются по-другому. - person Dietmar Kühl; 17.08.2013
comment
@DietmarKühl Спасибо за полезную информацию. Только начал учиться. Любые другие полезные комментарии к моему ответу были бы очень полезны. - person Saksham; 17.08.2013
comment
@DietmarKühl, C ++ 11 по-прежнему использует POD, если только значение не изменилось, и это еще один термин, который вы имеете в виду для того, что раньше было POD. - person chris; 17.08.2013
comment
@DietmarKühl: POD все еще существует, но стандарт C ++ 11 дополнительно делит концепцию POD на подконцепции standard-layout и trivial-type (POD - это оба). - person etherice; 17.08.2013
comment
@chris: Хорошо, я только что проверил: определяется POD. Для многих интересных применений этот термин был обобщен, я думаю, до стандартных типов компоновки, но они не могут быть инициализированы с помощью memset() вообще (может только подмножество POD). - person Dietmar Kühl; 17.08.2013
comment
@chris, не могли бы вы или кто-то еще выделить часть vtable в моем ответе. Это правильно или нет?? - person Saksham; 17.08.2013
comment
@Saksham: Это правильно. Вы можете безопасно использовать только типы memset, соответствующие standard-layout (см. en.cppreference.com/w/ cpp/concept/PODType, где это упоминается). Полиморфные типы особенно небезопасны из-за vpointer(ов) в макете объекта. - person etherice; 17.08.2013
comment
@Saksham: у тебя есть ;) - person MikeTusar; 17.08.2013