diff --git a/syntax/zero_size.md b/syntax/zero_size.md index 3b0aa28..cd60958 100644 --- a/syntax/zero_size.md +++ b/syntax/zero_size.md @@ -10,6 +10,7 @@ struct MyTag {}; ```C++ struct Tag {}; + Tag func(Tag t1) { Tag t2; return Tag{}; @@ -64,20 +65,22 @@ struct Vector3 : StdAllocator { Ну ладно, мы выяснили, что, неаккуратно использовав пустые структуры, мы можем получить увеличение потребления памяти. Давайте играть дальше. ```C++ +struct StdAllocator {}; +struct StdComparator {}; + struct Map1 { StdAllocator alloc; StdComparator comp; }; - struct Map2 { StdAllocator alloc; - [[no_unique_address]] StdComparator comp; + [[no_unique_address]] StdComparator comp; }; struct Map3 { - [[no_unique_address]] StdAllocator alloc; - [[no_unique_address]] StdComparator comp; + [[no_unique_address]] StdAllocator alloc; + [[no_unique_address]] StdComparator comp; }; struct MapImpl1 : Map1 { @@ -97,13 +100,13 @@ struct MapImpl3 : Map3 { Ну, тут все просто!: - Очевидно, что `sizeof(Map1) == 2`, ведь она состоит из двух пустых структур, каждая из которых имеет размер 1. -- Благодаря аттрибуту `[[no_unique_address]]` из C++20, `Map2` и `Map3` должны иметь размеры 1. В `Map3` оба поля разделяют общий адрес. В `Map2` то же самое. Да и меньще чем 1 не бывает. +- Благодаря атрибуту `[[no_unique_address]]` из стандарта C++20 (Clang поддерживает с C++11), `Map2` и `Map3` должны иметь размер 1. В `Map3` оба поля разделяют общий адрес. В `Map2` то же самое. Да и меньще чем 1 не бывает. Хорошо. А что же теперь с наследующими структурами? Все по `2*sizeof(int)`? А вот и нет: у `MapImpl3` [работает EBO](https://godbolt.org/z/exo5zP64E)! -Ну ладно. В этом есть какая-то логика и закономерность. Это еще можно принять. Хотя... На самом деле вы были правы! Ведь если у вас компилятор msvc, то `[[no_unique_address]]` просто [не работает](https://godbolt.org/z/6364qzYe6). И не будет работать. Потому что msvc долгое время просто игнорировал не знакомые ему аттрибуты. И если поддержать `[[no_unique_address]]`, то сломается бинарная совместимость. Используйте `[[msvc::no_unique_address]]`! EBO, правда, пока [не работает](https://godbolt.org/z/GTjrsbPPK). +Ну ладно. В этом есть какая-то логика и закономерность. Это еще можно принять. Хотя... На самом деле вы были правы! Ведь если у вас компилятор msvc, то `[[no_unique_address]]` просто [не работает](https://godbolt.org/z/6364qzYe6). И не будет работать. Потому что msvc долгое время просто игнорировал незнакомые ему атрибуты. И если поддержать `[[no_unique_address]]`, то сломается бинарная совместимость. Используйте `[[msvc::no_unique_address]]`! EBO, правда, пока [не работает](https://godbolt.org/z/GTjrsbPPK). ## zero-size array @@ -115,15 +118,14 @@ struct ImageHeader{ int h; int w; }; + struct Image { struct ImageHeader header; char data[]; }; ``` -Поле `data` в структуре `Image` имеет [нулевой размер](https://godbolt.org/z/d3xfdj3Ke). Это FAM (flexible array member). Очень удобная штука, чтобы получать доступ к массиву статически не известной длины, размешенному сразу послу некоторого заголовка в бинарном буфере. Длина массива обычно указавывается в самом заголовке. - -FAM может быть только последним полем в структуре. +Поле `data` в структуре `Image` имеет [нулевой размер](https://godbolt.org/z/d3xfdj3Ke). Это FAM (flexible array member). Очень удобная штука, чтобы получать доступ к массиву статически не известной длины, размещенному сразу послу некоторого заголовка в бинарном буфере. Длина массива обычно указавывается в самом заголовке. FAM может быть только последним полем в структуре. Стандарт C++ такие фичи не разрешает. Но ведь есть GCC с его нестандартными включенными по умолчанию расширениями. @@ -135,7 +137,7 @@ struct S { ``` Чему будет равень размер структуры `S`? -В стандратном C пустые структуры в принципе запрещены. И поведение программы с ними не определено. GCC определяет их размер нулевым при компиляции C программ. А при компиляции C++ — размер, как мы выяснили ранее, единичный. Дело пахнет страшными багами и ночными кошмарами при неосторожном проектировании C++ библиотек с сишным интерфейсом или использованием C-библиотек в C++! +В стандартном C пустые структуры в принципе запрещены. И поведение программы с ними не определено. GCC определяет их размер нулевым при компиляции C программ. А при компиляции C++ — размер, как мы выяснили ранее, единичный. Дело пахнет страшными багами и ночными кошмарами при неосторожном проектировании C++ библиотек с сишным интерфейсом или использованием C-библиотек в C++! Но вернемся все-таки к нашей структуре с FAM. Поле в ней есть. Стандартный C опять-таки требует, чтобы было еще хотя бы одно поле ненулевой длины перед FAM. GNU C же охотно сделает нам структуру нулевого размера. @@ -152,7 +154,7 @@ static_assert(sizeof(S1) != sizeof(S2)); static_assert(sizeof(S1) == 0); ``` -И вот уже внезапно у нас в C++ стуктуры нулевого размера. Только C++ не стандартный. +И вот уже внезапно у нас в C++ структуры нулевого размера. Только C++ не стандартный. Каким образом такие структуры будет взаимодействовать с EBO — нужно читать в спецификации к GCC. ## tag dispatching @@ -193,6 +195,6 @@ int add(int x, int y) { ## Полезные ссылки -1. https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/ +1. https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch 2. https://en.cppreference.com/w/cpp/language/attributes/no_unique_address -3. https://en.cppreference.com/w/cpp/language/ebo \ No newline at end of file +3. https://en.cppreference.com/w/cpp/language/ebo