Действительно ли `sizeof` * * оценивает` std:: size_t`? Может это?
Возьмем следующий стандартный проход:
[C++11: 5.3.3/6]:
Результат sizeof
и sizeof...
является константой типа std::size_t
. [Примечание: std::size_t
определяется в стандартном заголовке <cstddef>
(18.2). -end note]
Сейчас:
[C++11: 18.2/6]:
Тип size_t
представляет собой целочисленный тип без знака, определенный для реализации, который достаточно велик, чтобы содержать размер в байтах любого объекта.
Конечно, в проходе не требуется, чтобы size_t
был псевдонимом типа, определенным с помощью typedef
, но поскольку он явно заявил, что он доступен стандартным заголовком <cstddef>
, я думаю, мы можем считать, что прочитаем, что если не включить <cstddef>
, следует удалить любую гарантию, что size_t
будет доступным для программы.
Однако, согласно этой первой цитате, мы можем независимо получить выражение типа std::size_t
.
Мы можем фактически продемонстрировать оба этих факта:
int main()
{
typedef decltype(sizeof(0)) my_size_t;
my_size_t x = 0; // OK
std::size_t y = 1; // error: 'size_t' is not a member of 'std'
}
std::size_t
не отображается программе, но sizeof(0)
все еще дает нам один? Действительно?
Нельзя ли сказать, что 5.3.3/6
является ошибочным и что он фактически имеет "тот же тип, что и любой std::size_t
разрешается", но не std::size_t
сам?
Конечно, эти два являются одинаковыми, если std::size_t
является псевдонимом типа, но, опять же, этого не требуется.
Ответы
Ответ 1
Не путайте карту для территории.
Типы могут быть названы по именам. Эти типы имен могут быть встроены, они могут быть определяемыми пользователем типами, или они могут даже быть параметрами template
и ссылаться на несколько разных типов в зависимости от экземпляра.
Но имена не являются типами. Очевидно, что стандарт не предусматривает, что все типы имеют имена - классический struct {}
- это тип без имени.
std::size_t
- это имя. Он называет тип, возвращаемый sizeof(expression)
.
У компилятора может быть каноническое имя для типа - __size_t
будет одним из способов для него иметь уникальное встроенное каноническое имя типа.
Стандартные гарантии в этом разделе, что независимо от типа sizeof(expression)
, после #include <cstddef>
имя std::size_t
теперь относится к этому типу.
В стандарте они относятся к типам по именам. Они не говорят "тип, к которому относится это typename", но просто говорят "тип $NAME $". Компилятор мог решить, что int
- это другое имя для __int_32_fast
, если оно того захочет, и стандарт тоже не будет возражать.
То же самое происходит с std::nullptr_t
и std::initializer_list<Ts>
и std::type_info
: использование переменных этих типов не всегда требует, чтобы заголовок, который предоставил вам имя для этих типов, включался в вашу программу.
Традиционные встроенные типы C/С++ имели канонические имена, для которых не требовался заголовок. Недостатком является то, что это нарушает существующий код, поскольку новые типы имен в глобальной области взаимодействуют с другими идентификаторами.
Имея "безымянные типы", где вы можете получить имя для них, включая заголовочный файл, мы избегаем этой проблемы.
Ответ 2
Стандарт просто указывает, что тип sizeof(expr)
является тем же типом, что и std::size_t
. Нет мандата, что использование sizeof(expr)
делает доступным имя std::size_t
, и поскольку std::size_t
просто называет один из встроенных интегральных типов, проблема не возникает.
Ответ 3
Как я понимаю, этот стандартный проход требует следующего выражения:
typeid(sizeof(0)) == typeid(std::size_t)
всегда будет true
. Если вы используете фактический идентификатор std::size_t
, ::size_t
или любой другой псевдоним /typedef будет неактуальным, если сохраняется идентичность типа, как std::typeinfo::operator==()
.
Проблема с идентичным типом типа появляется в других местах языка. Например, на моей 64-разрядной машине следующий код не может скомпилироваться из-за переопределения функции:
#include <cstddef>
void foo(std::size_t x)
{}
void foo(unsigned long x)
{}
Ответ 4
Да.
Тип, предоставляемый sizeof
, представляет собой целое число без знака; реализация определяет, какая из них.
Например, для некоторой конкретной реализации тип выражения sizeof
может быть unsigned long
.
std::size_t
, если он a typedef
, является не более чем альтернативным именем для unsigned long
. Итак, эти два утверждения:
Тип sizeof ...
является константой типа unsigned long
и
Тип sizeof ...
является константой типа std::size_t
говорят точно такую же вещь для этой реализации. Тип unsigned long
и тип std::size_t
являются одним и тем же типом. Разница в том, что последняя является точной для всех (соответствующих) реализаций, где std::size_t
может быть псевдонимом для, скажем, unsigned int
или другого неподписанного типа.
Что касается компилятора, sizeof
дает результат типа unsigned long
; компилятор (в отличие от библиотеки времени выполнения) не должен иметь никакого знания имени size_t
.
Все это предполагает, что std::size_t
(или просто size_t
, если вы говорите о C) является typedef. Это не указано ни в C, ни в стандарте С++. Тем не менее, реализация может прямо соответствовать требованиям стандарта, делая size_t
typedef. Я не верю, что есть какой-либо другой переносной способ удовлетворить эти требования. (Это не может быть макрос или ключевое слово, определяемое реализацией, поскольку это будет нарушать пространство имен пользователей, а макрос не будет охвачен в пространстве имен std
.) Компилятор может сделать size_t
конкретную конструкцию, отличную от typedef, но так как typedef работает отлично, нет смысла делать это. Было бы неплохо, ИМХО, если бы в стандарте указано, что size_t
является typedef.
(Не имеет значения. Реальная проблема заключается в том, что стандарт ссылается на результат как на "константу". В ISO C "константа" представляет собой токен, такой как целочисленный литерал. С++, насколько я знаю, не определяет существительное "константа", но оно относится к определению термина C. ISO sizeof ...
- это константное выражение, оно не является константой. Вызов результата "постоянное значение" был бы разумным. )
Ответ 5
Это тот же тип, но вы должны включить этот заголовок для его использования.