Який заголовок я повинен включити для `size_t`?


95

Згідно cppreference.com size_t визначається в декількох заголовках, а саме

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

І, починаючи з C ++ 11, також у

<cstdlib>
<cwchar> 

Перш за все мені цікаво, чому це так. Хіба це не суперечить принципу СУХОСТІ ? Однак моє запитання:

Який із наведених вище заголовків слід включити для використання size_t? Це взагалі має значення?


1
Відкрийте відповідні файли заголовків і знайдіть визначення.
i486,

33
@ 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

Відповіді:


90

Припускаючи, що я хочу мінімізувати функції та типи, які я імпортую, я б пішов, cstddefоскільки вони не декларують жодних функцій, а декларують лише 6 типів. Інші фокусуються на певних доменах (рядки, час, введення / виведення), які можуть для вас не мати значення.

Зверніть увагу, що cstddefгарантує лише визначення std::size_t, тобто визначення size_tу просторі імен std, хоча воно може вказувати це ім’я також у глобальному просторі імен (фактично, просто size_t).

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


3
Чи є якась гарантія того, що size_tвід cstddefє однаковим і завжди буде таким же, як і інші? Здається, повинен бути загальний файл заголовка із загальними визначеннями, як-от size_t...
SnakeDoc

1
@SnakeDoc і ніби за допомогою магії, інша відповідь тут вже спостерігала саме те, що відбувається, через "внутрішній" заголовок.
underscore_d

5
@SnakeDoc Так, і цей заголовок є cstddef.
user253751

2
@SnakeDoc, хто каже, що вони визначають своє? Все, що відповідає стандарту, - це буде визначено після включення цих заголовків, не сказано, що всі вони повинні перевизначити його. Всі вони можуть включати <cstddef>, або всі вони можуть включати якийсь внутрішній заголовок, який просто визначає size_t.
Джонатан Уейклі

1
Чи відповідає csttddefу відповіді помилка? Може cstddef, мається на увазі?
Ерік Сьолунд,

46

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

Стандарт C ++ , проте, відноситься до <cstddef>визначенняstd::size_t

  • у 18.2 типів ,
  • у 5.3.3 Розмір ,
  • у 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 :

    Поведінка програми С ++ невизначена, якщо вона додає декларації або визначення до простору імен std або до простору імен у просторі імен std, якщо не вказано інше.

    Додавання належного typedef для size_tдо простору імен не порушує 7.1.3, але порушує 17.6.4.2.1 і призводить до невизначеної поведінки.

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


1
Ви пропускаєте той факт, що дублікат typedef не вводить нового типу. Він просто додає копію typedef, що цілком дійсне.
Максим Єгорушкін

@MaximEgorushkin: Я не стверджую, що додавання перевизначення typedef до stdнедійсне, оскільки повторювані typedefs є незаконними. Я заявляю, що це незаконно, оскільки ви просто не можете додавати визначення namespace std- незалежно від того, чи будуть вони законними.
Піксельхімік,

Що потенційно може зламатися, враховуючи все, що ми знаємо з усіх цих стандартних цитат?
Максим Єгорушкін

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

2
не впевнений, але я думаю, це має значення для часу компіляції, ні?
idclev 463035818

@ tobi303 не для цього конкретного питання. Так, ви можете додати заголовок більший, ніж потрібно, але тоді ви вже додали заголовок C у проект С ++. Навіщо вам size_tв першу чергу?
Панайотис Канавос,

Це не гарна ідея використовувати для визначення нюху макросів ОС size_t. Ви можете визначити це більш портативно як using size_t = decltype( sizeof( 42 ) ). Але немає необхідності, оскільки <stddef.h>має майже нульову вартість.
Вітаю і hth. - Альф

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оголошенням того самого типуdef-name (помилка компілятора видається в невідповідних оголошеннях).

Це відбувається тому:

  • §7.1.3.1 Ім'я typedef не вводить новий тип так, як це робить декларація класу (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, і немає стандартних вбудованих typedefs. Отже, найбільш портативним рішенням на той час було помістити size_tпсевдонім типу в якийсь конкретний заголовок і документувати це.

У C ++ 11 тепер є спосіб записати цю точну вимогу стандарту як одну просту декларацію.


6
@Sean Те, що ви написали, не має жодного сенсу.
Максим Єгорушкін

15
@ MaximEgorushkin Половина з них не зрозуміли цей код ... він працює ідеально. Однак мені не подобається такий спосіб: краще, imo, включити заголовок і нехай стандарт визначає його.
Боєтіос,

9
Хлопці, принаймні вивчіть цю мову, перш ніж проголосувати за абсолютно правильні відповіді.
Фредерік Хаміді,

11
Том сказав: "Є 6 стандартних заголовків бібліотеки, що визначають одне і те ж! Це божевільно! Нам потрібно одне і єдине визначення size_t!" Через хвилину Мері сказала: "OMG! Існує 7 визначень size_tзагальноприйнятих заголовків бібліотек, а заголовок проекту редагує Том! У бібліотеках сторонніх розробників, можливо, більше!" xkcd.com/927

6
Незважаючи на те, що це можливе визначення size_t, це не відповідає справжньому питанню OP: це наче я запитував заголовок, де FILEоголошено, і ви пропонуєте написати мій власний.
edmz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.