Does `sizeof` *really* evaluate to a `std::size_t`? Can it?

C++C++11StdLanguage Lawyer

C++ Problem Overview


Take the following standard passage:

> [C++11: 5.3.3/6]: The result of sizeof and sizeof... is a constant of type std::size_t. [ Note: std::size_t is defined in the standard header <cstddef> (18.2). —end note ]

Now:

> [C++11: 18.2/6]: The type size_t is an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.

Granted, the passage doesn't require that size_t is a type alias defined with typedef, but since it's explicitly stated to be made available by the standard header <cstddef>, I think we can take as read that failing to include <cstddef> should remove any guarantee that size_t shall be available to a program.

However, according to that first quote, we can regardless obtain an expression of type std::size_t.

We can actually demonstrate both of these facts:

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 is not visible to the program, but sizeof(0) still gives us one? Really?

Is it therefore not correct to say that 5.3.3/6 is flawed, and that it actually has "the same type as whatever std::size_t resolves to", but not std::size_t itself?

Sure, the two are one and the same if std::size_t is a type alias but, again, nowhere is this actually required.

C++ Solutions


Solution 1 - C++

The standard just mandates that the type of sizeof(expr) is the same type as std::size_t. There is no mandate that using sizeof(expr) makes the name std::size_t available and since std::size_t just names one of the the built-in integral types there isn't really a problem.

Solution 2 - C++

Do not confuse the map for the territory.

Types can be named by typenames. These typenames can be built-in, they can be user-defined types, or they could even be template parameters and refer to multiple different types depending on the instantiation.

But the names are not the types. Clearly standard does not mandate that all types have names -- the classic struct {} is a type without a name.

std::size_t is a typename. It names the type that sizeof(expression) returns.

The compiler could have a canonical name for the type -- __size_t would be one way for it to have a unique built-in canonical typename.

The standard guarantees in that clause that whatever the type of sizeof(expression) is, once you #include <cstddef>, the name std::size_t now refers to that type.

In the standard, they refer to types by names. They do not say "the type that this typename refers to", but simply say "the type $NAME$". The compiler could decide that int is another name for __int_32_fast if it wanted to, and the standard would have no objection either.

This same thing happens with std::nullptr_t and std::initializer_list<Ts> and std::type_info: use of variables of those types does not always require that the header that provides you with a name for those types be included in your program.

The traditional C/C++ built-in types all had canonical names that did not require a header. The downside is that this breaks existing code, as new typenames in the global scope collide with other identifiers.

By having "nameless types", where you can get a name for them via including a header file, we avoid that problem.

Solution 3 - C++

As I understand it, this standard passage requires the following expression:

typeid(sizeof(0)) == typeid(std::size_t)

will always yield true. If you use the actual identifier std::size_t, ::size_t or any other alias/typedef will be irrelevant as long as the identity of the type, as per std::typeinfo::operator==(), is preserved.

The same type identity issue appears in other places of the language. For example, in my 64-bit machine the following code fails to compile because of function redefinition:

#include <cstddef>
void foo(std::size_t x)
{}

void foo(unsigned long x)
{}

Solution 4 - C++

Yes.

The type yielded by sizeof is some unsigned integer type; the implementation defines which one it is.

For example, on some particular implementation, the type of a sizeof expression might be unsigned long.

std::size_t, if it's a typedef, is nothing more than an alternative name for unsigned long. So these two statements:

> The type of sizeof ... is a constant of type unsigned long

and

> The type of sizeof ... is a constant of type std::size_t

are saying exactly the same thing for that implementation. The type unsigned long and the type std::size_t are the same type. The difference is that the latter is accurate for all (conforming) implementations, where std::size_t might be an alias for, say, unsigned int or some other unsigned type.

As far as the compiler is concerned, sizeof yields a result of type unsigned long; the compiler (as opposed to the runtime library) needn't have any knowledge of the name size_t.

This all assumes that std::size_t (or just size_t if you're talking about C) is a typedef. That's not spelled out in either the C or the C++ standard. Nevertheless, an implementation can straightforwardly conform to the requirements of the standard by making size_t a typedef. I don't believe there's any other portable way to satisfy those requirements. (It can't be a macro or an implementation-defined keyword because that would infringe on the user's name space, and a macro wouldn't be scoped within the std namespace.) A compiler could make size_t some implementation-specific construct other than a typedef, but since a typedef works perfectly well, there's no point in doing so. It would be nice, IMHO, if the standard stated that size_t is a typedef.

(An irrelevant aside: The real problem is that the standard refers to the result as a "constant". In ISO C, a "constant" is a token, such as an integer literal. C++, as far as I know, doesn't define the noun "constant", but it does refer to the ISO C definition of the term. sizeof ... is a constant expression; it's not a constant. Calling the result a "constant value" would have been reasonable.)

Solution 5 - C++

It is the same type but you have to include that header to use it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLightness Races in OrbitView Question on Stackoverflow
Solution 1 - C++Dietmar KühlView Answer on Stackoverflow
Solution 2 - C++Yakk - Adam NevraumontView Answer on Stackoverflow
Solution 3 - C++rodrigoView Answer on Stackoverflow
Solution 4 - C++Keith ThompsonView Answer on Stackoverflow
Solution 5 - C++user2030677View Answer on Stackoverflow