A constant expression is an expression that can be evaluated at compile time. Constants of integral or enumerated type are required in several different situations, such as array bounds, enumerator values, and case labels. Null pointer constants are a special case of integral constants.
An integral constant expression is an expression that can be evaluated at compile time, and whose type is integral or an enumeration. The situations that require integral constant expressions include array bounds, enumerator values, case labels, bit-field sizes, static member initializers, and value template arguments. The compiler must be able to evaluate the expression at compile time, so you can use only literals, enumerators, const objects that have constant initializers, integral or enumerated template parameters, sizeof expressions, and constant addresses. The address of a static lvalue object is a constant address, as is the address of a function. A string literal, being a static array of characters, is also a constant address.
An integral static const data member can be initialized in the class definition if the initializer is a constant integral or enumerated expression. The member can then be used as a constant expression elsewhere in the class definition. For example:
template<typename T, size_t size> class array { public: static const size_t SIZE = size; ... private: T data[SIZE]; };
See Chapter 6 for more information about static data members.
A constant expression with a value of 0 can be a null pointer constant. A null pointer constant can be converted to a null pointer value. The colloquial term null pointer almost always means null pointer value.
A null pointer value has an implementation-defined bit pattern. Many implementations use all zero bits, but some do not. Thus, the null pointer constant is not representative of the bits that make up a null pointer value, but serves only as a mnemonic for the programmer, much like = 0 does for a pure virtual function (Chapter 6).
When you assign a null pointer constant to a pointer-type variable, the compiler converts the null pointer constant to a null pointer value of the appropriate type. Similarly, when comparing pointers, the compiler ensures that the comparison is meaningful. In particular, a null pointer value is never equal to any valid pointer value, is always equal to another null pointer value, and is always equal to a null pointer constant. A null pointer value, when converted to bool, is false. A pointer that is initialized with an empty initializer is initialized to a null pointer value.
The NULL macro, defined in <cstdlib> and other headers (see Chapter 13), expands to a null pointer constant. Using NULL instead of 0 can be a helpful reminder to the reader or maintainer of a program, especially when typedefs obscure the nature of a type:
Token tok1 = 0; // Is Token a pointer or an integer? Token tok2 = NULL; // Token is probably a pointer.
Dereferencing a null pointer results in undefined behavior.