Какой заголовок я должен включить для size_t?

98

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

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

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

<cstdlib>
<cwchar> 

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

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

idclev 463035818
источник
2
Откройте соответствующие файлы заголовков и найдите определение.
i486
35
@ i486 - отличный способ написать непереносимый непереносимый код!
Шон
3
Заголовки @PanagiotisKanavos C, которые являются частью стандартной библиотеки C ++ и, вероятно, не дублируются ни в одном из ваших предполагаемых заголовков «истинного C ++». В чем конкретно заключалась ваша точка зрения?
underscore_d
14
Я всегда использовал <cstddef>дляstd::size_t
Boiethios
4
@PanagiotisKanavos Конечно, в целом это хороший совет, но в данном случае он не кажется актуальным - поскольку нет замены C ++ std::size_t, и OP не выступал за использование устаревших функций C, просто наблюдая за цитатой о том, что они разделяют typedef. Я сомневаюсь, что из-за этого кто-то, читающий эту ветку, будет введен в заблуждение относительно использования устаревших типов / функций, но если вы хотите быть уверены, что они этого не делают, тогда достаточно справедливо!
underscore_d

Ответы:

94

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

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

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

Шон
источник
3
Есть ли гарантия, что size_tfrom cstddefтакое же и всегда будет таким же, как и другие? Похоже, должен быть общий заголовочный файл с общими определениями вроде size_t...
SnakeDoc
1
@SnakeDoc, и, как по волшебству, другой ответ здесь уже наблюдал именно это, через «внутренний» заголовок.
underscore_d
5
@SnakeDoc Да, и этот заголовок есть cstddef.
user253751
2
@SnakeDoc, кто сказал, что они определяют свои собственные? Стандарт говорит, что он будет определен после включения этих заголовков, но не говорит, что все они должны его переопределить. Все они могут включать <cstddef>или все они могут включать некоторый внутренний заголовок, который просто определяет size_t.
Джонатан Уэйкли
1
Является ли csttddefв ответе опечатка? Может cstddefимеется ввиду?
Erik
47

Фактически, синопсис (включенный в стандарт C ++) нескольких заголовков специально включает, size_tа также дополнительные заголовки определяют тип size_t(на основе стандарта C, поскольку <cX>заголовки представляют собой просто <X.h>заголовки ISO C с отмеченными изменениями, удаление 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без заголовка

    Если вы планируете ввести ЬурейиЙ в коде в любом случае (то есть , потому что вы пишете контейнер и хотите , чтобы обеспечить size_typeЬурейиЙ) , вы можете использовать глобальные 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хотя возможно иметь несколько определений типов, относящихся к одному и тому же типу в одном пространстве имен.

    Хотя наличие нескольких определений 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

Pixelchemist
источник
1
Вы упускаете тот факт, что дублирующийся typedef не вводит новый тип. Он просто добавляет повторяющийся typedef, что совершенно верно.
Максим Егорушкин
@MaximEgorushkin: Я не утверждаю, что добавление переопределения typedef в stdнедопустимо, потому что дублирование typedef недопустимо. Я заявляю, что это незаконно, потому что вы просто не можете добавлять определения к namespace std- независимо от того, будут ли они законными.
Pixelchemist
Что потенциально может сломаться, учитывая все, что мы знаем из всех этих стандартных цитат?
Максим Егорушкин
12
@MaximEgorushkin: Что угодно. В этом суть неопределенного поведения, не так ли? Дело в том, что она может работать или даже дело , что никак не ломается на любом произвольном компиляторе не делает поведение программы , определенной в соответствии со стандартом. Или как «fredoverflow» положить его красиво здесь : «Стандарт C ++ имеет единственный голос, период.»
Pixelchemist
Я бы хотел, чтобы вы использовали свое критическое мышление. Что потенциально могло сломаться?
Максим Егорушкин
9

Все заголовочные файлы стандартной библиотеки имеют одинаковое определение; не имеет значения, какой из них вы включите в свой собственный код. На моем компьютере есть следующее объявление в формате _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
vll
источник
2
не уверен, но я думаю, что это имеет значение для времени компиляции, не так ли?
idclev 463035818
@ tobi303 не для этого конкретного вопроса. Да, вы можете добавить заголовок большего размера, чем необходимо, но тогда вы уже добавили заголовок C в проект C ++. Зачем тебе это вообще нужно size_t?
Панайотис Канавос,
Не рекомендуется использовать для определения анализ макросов ОС size_t. Вы можете определить его более переносимо как using size_t = decltype( sizeof( 42 ) ). Но в этом нет необходимости, так как <stddef.h>он практически нулевой.
Приветствия и hth. - Alf
4

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

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) или объявления enum.

  • §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)); }- одна из тех вещей, которые ничего не нарушают.

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

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

Максим Егорушкин
источник
6
@Sean То, что ты написал, не имеет никакого смысла.
Максим Егорушкин
15
@MaximEgorushkin Половина из них не поняла этот код ... он отлично работает. Однако мне не нравится такой способ: лучше, я думаю, включить заголовок и позволить стандарту определять его.
Бойетиос,
9
Ребята, выучите хотя бы этот гребаный язык, прежде чем голосовать против совершенно правильных ответов.
Фредерик Хамиди,
11
Том сказал: «Есть 6 заголовков стандартных библиотек, определяющих одно и то же! Это безумие! Нам нужно одно и только одно определение size_t!» Минутой позже Мэри сказала: «Боже мой! Есть 7 определений size_tзаголовков стандартных библиотек и заголовок проекта, который Том редактирует! Возможно, их больше в сторонних библиотеках!» xkcd.com/927
6
Хотя это возможное определение size_t, это не отвечает на настоящий вопрос OP: это как если бы я попросил заголовок, в котором FILEобъявлен, и вы бы предложили написать свой собственный.
edmz