Подтвердить что ты не робот

Какой заголовок следует включить для `size_t`?

В соответствии с cppreference.com size_t определяется в нескольких заголовках, а именно

<cstddef>
<cstdio>
<cstring>
<ctime>

И, поскольку С++ 11, также в

<cstdlib>
<cwchar> 

Прежде всего мне интересно, почему это так. Разве это не противоречит принципу DRY? Однако, мой вопрос:

Какой из перечисленных выше заголовков я должен использовать для использования size_t? Это имеет значение вообще?

4b9b3361

Ответ 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 теперь есть способ записать это точное требование стандарта как одно простое объявление.