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

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

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

и, начиная с C++11, также в

<cstdlib>
<cwchar> 

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

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

4 ответов


предполагая, что я хотел минимизировать функции и типы, которые я импортировал, я бы пошел с cstddef поскольку он не объявляет никаких функций и объявляет только 6 типов. Другие сосредоточены на определенных доменах (strings, time, IO), которые могут не иметь для вас значения.

отметим, что cstddef только гарантии для определения std::size_t, то есть по определению size_t в пространстве имен std, хотя мая укажите это имя также в глобальном пространстве имен (эффективно, простой size_t).

в противоположность stddef.h (который также является заголовком, доступным в C) гарантирует определение size_t в глобальном пространстве имен, и мая предоставляем std::size_t.


на самом деле синопсис (включенный в стандарт C++) нескольких заголовков конкретно включает size_t а также дальнейшие заголовки определяют тип size_t (на основе стандарта C как <cX> заголовки - это просто ISO C <X.h> заголовки с отмеченными изменениями, где удаление size_t Не указано).

стандарт C++, относится к <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 доступен.


обратите внимание на несколько моментов :

  1. тип std::size_t можно получить с помощью decltype без заголовка

    если вы все равно планируете ввести typedef в свой код (т. е. потому, что вы пишете контейнер и хотите предоставить size_type typedef) вы можете использовать глобальный sizeof, sizeof... или alignof операторы для определения типа без включения каких-либо заголовков вообще, так как операторы theose возвращают std::size_t в стандартное определение и вы может использовать decltype на них:

    using size_type = decltype(alignof(char));
    
  2. 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 неправильно сформирован, если имя не было объявлено путем включения соответствующего заголовок.

  3. пользователь не может переопределить std::size_t хотя возможно иметь несколько typedefs, ссылающихся на один и тот же тип в одном пространстве имен.

    хотя, возникновение нескольких определений size_t внутри std совершенно действителен согласно 7.1.3 / 3, не разрешается добавлять какие-либо объявления в namespace std по состоянию на 17.6.4.2.1 / 1:

    в поведение программы C++ не определено, если она добавляет объявления или определения в пространство имен std или в пространство имен в пространстве имен std, если не указано иное.

    добавление правильного typedef для size_t чтобы пространство имен не нарушало 7.1.3 но это не нарушает 17.6.4.2.1 и приводит к неопределенному поведению.

    уточнение: постарайтесь не интерпретировать 7.1.3 и не добавлять объявления или определения к std (за исключением нескольких случаев специализации шаблона, когда typedef не является специализацией шаблона). расширения namespace std


все стандартные файлы заголовков библиотек имеют одинаковое определение; не имеет значения, какой из них вы включаете в свой собственный код. На моем компьютере у меня есть следующее объявление в _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

можно обойтись и без заголовка:

using size_t = decltype(sizeof(int));
using size_t = decltype(sizeof 1); //  The shortest is my favourite.
using size_t = decltype(sizeof "anything");

это потому, что стандарт C++ требует:

результат 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-name (ошибка компилятора выдается при несоответствующих объявлениях).

это потому, что:

  • §7.1.3.1 в typedef-name не вводит новый тип так, как это делает объявление класса (9.1) или объявление перечисления.

  • §7.1.3.3 в данной неклассовой области a typedef спецификатор можно использовать для переопределите имя любого типа, объявленного в этой области, чтобы ссылаться на тип, к которому он уже относится.


скептикам, говорящим, что это представляет собой добавление нового типа в пространство имен std, и такой акт явно запрещен стандартом, и это UB, и это все; я должен сказать, что это отношение равносильно игнорированию и отрицанию более глубокого понимания основополагающих вопросов.

стандартные запреты добавления новые объявления и определения в пространство имен std потому что, делая это, пользователь может сделать беспорядок стандартной библиотеки и отстрелить всю ногу. Для стандартных авторов было проще позволить пользователю специализироваться на нескольких конкретных вещах и запретить делать что-либо еще для хорошей меры, а не запрещать каждую вещь, которую пользователь не должен делать, и рисковать пропустить что-то важное (и эту ногу). Они делали это в прошлом, требуя, чтобы ни один стандартный контейнер не был создан с неполным типом, хотя на самом деле некоторые контейнеры вполне могли бы сделать (см. стандартный библиотекарь: контейнеры неполных типов Мэтью Х. Остерн):

... В конце концов, все это казалось слишком мрачным и слишком плохо понимаемым; комитет по стандартизации не думал, что был какой-либо выбор, кроме как сказать, что контейнеры STL не должны работать с неполными типами. Для хорошей меры мы применили этот запрет к остальной части стандартной библиотеки тоже.

... Оглядываясь назад, теперь, когда технология лучше понята, это решение все еще кажется в основном правильным. Да, в некоторых случаях можно реализовать некоторые стандартные контейнеры, чтобы их можно было создать с неполными типами, но также ясно, что в других случаях это будет сложно или невозможно. В основном это был шанс, что первый тест мы попробовали, используя std::vector, случилось быть одним из легких случаев.

учитывая, что языковые правила требуют std::size_t ровно decltype(sizeof(int)), doing namespace std { using size_t = decltype(sizeof(int)); } это одна из тех вещей, которые ничего не сломать.

до C++11 не было decltype и, таким образом, нет способа объявить тип sizeof результат в одном простом заявлении, не получая много шаблонов, участвующих. size_t псевдонимы разных типов на разных целевых архитектурах, однако, это не было бы элегантным решением добавить новый встроенный тип только для результата sizeof и есть никаких стандартных встроенных типов. Следовательно, самым портативным решением в то время было поставить size_t введите псевдоним в определенный заголовок и документ.

в C++11 теперь есть способ записать это точное требование Стандарта как одно простое объявление.