Какой заголовок следует включить для `size_t`?
В соответствии с cppreference.com size_t
определяется в нескольких заголовках, а именно
<cstddef>
<cstdio>
<cstring>
<ctime>
И, поскольку С++ 11, также в
<cstdlib>
<cwchar>
Прежде всего мне интересно, почему это так. Разве это не противоречит принципу DRY? Однако, мой вопрос:
Какой из перечисленных выше заголовков я должен использовать для использования size_t
? Это имеет значение вообще?
Ответы
Ответ 1
Предполагая, что я хочу свести к минимуму функции и типы, которые я импортировал, я бы пошел с cstddef
, поскольку он не объявляет никаких функций и только объявляет 6 типов. Другие сосредоточены на определенных доменах (строки, время, IO), которые могут не иметь для вас значения.
Обратите внимание, что csttddef
гарантирует только определение std::size_t
, то есть определение size_t
в пространстве имен std
, хотя оно может также содержать это имя в глобальном пространстве имен (эффективно, просто size_t
).
Напротив, stddef.h
(который также является заголовком, доступным на C) гарантирует определение size_t
в глобальном пространстве имен и может также предоставить std::size_t
.
Ответ 2
Фактически, синопсис (включенный в стандарт С++) нескольких заголовков, в частности, включает size_t
, а также другие заголовки определяют тип size_t
(на основе стандарта C, поскольку заголовки <cX>
являются только ISO C <X.h>
с отмеченными изменениями, когда удаление size_t
не указано).
Стандарт С++, однако относится к <cstddef>
для определения std::size_t
- в типах 18.2,
- в 5.3.3 Sizeof,
- в 3.7.4.2. Функции освобождения (что относится к 18.2) и
- в 3.7.4.1 Функции распределения (также относится к 18.2).
Поэтому и из-за того, что <cstddef>
вводит только типы и никакие функции, я должен придерживаться этого заголовка, чтобы сделать std::size_t
доступным.
Обратите внимание на несколько вещей:
-
Тип std::size_t
можно получить, используя decltype
без включения заголовка
Если вы планируете вводить typedef в свой код в любом случае (то есть, потому что вы пишете контейнер и хотите предоставить size_type
typedef), вы можете использовать глобальные операторы sizeof
, sizeof...
или alignof
для определения вашего типа без включения каких-либо заголовков вообще, поскольку операторы theose возвращают std::size_t
для стандартного определения, и вы можете использовать decltype
для них:
using size_type = decltype(alignof(char));
-
std::size_t
по сути не является глобально видимым, хотя функции с аргументами std::size_t
:
Неявно объявленные глобальные функции распределения и освобождения
void* operator new(std::size_t);
void* operator new[](std::size_t);
void operator delete(void*);
void operator delete[](void*);
НЕ вводите size_t
, std
или std::size_t
и
ссылаясь на std
или std::size_t
, плохо сформирован, если это имя не было объявлено включением соответствующего заголовка.
-
Пользователь не может переопределять std::size_t
, хотя можно иметь несколько типов typedef, ссылающихся на один и тот же тип в одном и том же пространстве имен.
Хотя появление множества определений size_t
внутри std
отлично действует в соответствии с 7.1.3/3, не допускается добавлять какие-либо объявления в namespace std
в соответствии с 17.6.4.2.1/1
Поведение программы на С++ undefined, если оно добавляет объявления или определения в пространство имен std или в пространство имен в пространстве имен std, если не указано иное.
Добавление правильного typedef для size_t
в пространство имен не нарушает 7.1.3, но оно нарушает 17.6.4.2.1 и приводит к поведению undefined.
Уточнение: не пытайтесь неверно истолковать 7.1.3 и не добавляйте объявления или определения к std
(за исключением нескольких случаев специализации по шаблонам, где typedef не является специализированной). Расширение namespace std
Ответ 3
Все стандартные файлы заголовков библиотеки имеют одинаковое определение; неважно, какой из них вы включите в свой собственный код. На моем компьютере у меня есть следующее объявление в _stddef.h
. Этот файл включен в каждый файл, который вы указали.
/*
Define the size_t type in the std namespace if in C++ or globally if in C.
If we're in C++, make the _SIZE_T macro expand to std::size_t
*/
#if !defined(_SIZE_T) && !defined(_SIZE_T_DEFINED)
# define _SIZE_T_DEFINED
#if defined(_WIN64)
typedef unsigned __int64 size_t;
#else
typedef unsigned int size_t;
#endif
# if defined(__cplusplus)
# define _SIZE_T std::size_t
# else
# define _SIZE_T size_t
# endif
#endif
Ответ 4
Вы можете обойтись без заголовка:
using size_t = decltype(sizeof(int));
using size_t = decltype(sizeof 1); // The shortest is my favourite.
using size_t = decltype(sizeof "anything");
Это связано с тем, что для стандарта С++ требуется:
Результат sizeof
и sizeof...
является константой типа std::size_t
. [Примечание: std::size_t
определяется в стандартном заголовке <cstddef>
(18.2). - конечная нота]
Другими словами, стандарт требует:
static_assert(std::is_same<decltype(sizeof(int)), std::size_t>::value,
"This never fails.");
Также обратите внимание, что это прекрасно сделать это объявление typedef
в глобальном и в std
пространстве имен, если оно соответствует всем другим объявлениям typedef
одного и того же typedef-имени (ошибка компилятора выпущенные при несогласованных декларациях).
Это происходит потому, что:
-
§7.1.3.1 Имя typedef не вводит новый тип так, как это делает объявление класса (9.1) или объявление перечисления.
-
§7.1.3.3 В заданной неклассовой области спецификатор typedef
может использоваться для переопределения имени любого типа, объявленного в этой области, для обозначения того типа, к которому он уже относится.
Скептикам говорят, что это представляет собой добавление нового типа в пространство имен std
, и такой акт явно запрещен стандартом, и это UB, и это все к нему; Я должен сказать, что это отношение состоит в игнорировании и отрицании более глубокого понимания основных проблем.
Стандартные запреты добавления новых деклараций и определений в пространство имен std
, потому что при этом пользователь может создать беспорядок в стандартной библиотеке и снять всю свою ногу. Для стандартных авторов было проще позволить пользователю специализироваться на нескольких конкретных вещах и запрещать делать что-либо еще для хорошей меры, а не запрещать каждую вещь, которую пользователь не должен делать, и рискнуть отсутствием чего-то важного (и той ноги). Они делали это в прошлом, когда требовали, чтобы стандартный контейнер не был создан с неполным типом, в то время как на самом деле некоторые контейнеры могли бы хорошо (см. Стандартный библиотекарь: Контейнеры неполных типов Мэтью Х. Аустерн):
... В конце концов, все это казалось слишком мутным и слишком слабо понятным; комитет по стандартизации не думал, что есть какой-либо выбор, кроме как сказать, что контейнеры STL не должны работать с неполными типами. Для хорошей меры мы применили этот запрет и для остальных стандартных библиотек.
... Оглядываясь назад, теперь, когда технология лучше понятна, это решение по-прежнему кажется правильным. Да, в некоторых случаях можно реализовать некоторые из стандартных контейнеров, чтобы они могли быть созданы с неполными типами, но также ясно, что в других случаях это было бы трудно или невозможно. В основном было случайным, что первый тест, который мы пробовали, используя std::vector
, оказался одним из простых случаев.
Учитывая, что языковые правила требуют, чтобы std::size_t
был точно decltype(sizeof(int))
, выполнение namespace std { using size_t = decltype(sizeof(int)); }
является одной из тех вещей, которые ничего не сломают.
До С++ 11 не было decltype
и, таким образом, не было способа объявить тип результата sizeof
в одном простом выражении без привлечения большого количества шаблонов. size_t
псевдонимы разных типов на разных целевых архитектурах, однако это не было бы элегантным решением для добавления нового встроенного типа только для результата sizeof
, и нет стандартных встроенных typedef. Следовательно, самым портативным решением в то время было размещение псевдонима типа size_t
в каком-то конкретном заголовке и документе.
В С++ 11 теперь есть способ записать это точное требование стандарта как одно простое объявление.