Basic Types and Values
|
This section documents C++23 (ISO/IEC 14882:2024), as published by ISO/IEC JTC1/SC22/WG21 (wg21), verified against the freely available working draft N5046 (eel.is/c++draft) and cppreference.com. This content was generated with the assistance of AI and should be verified against the working draft and cppreference.com before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Fundamental Types
bool flag = true;
char c = 'a'; // at least 8 bits; signedness is implementation-defined
signed char sc = -1;
unsigned char uc = 255;
short s = 1; // at least 16 bits
int i = 1; // at least 16 bits, typically 32
unsigned int u = 1u;
long l = 1L; // at least 32 bits
long long ll = 1LL; // at least 64 bits
float f = 1.0f; // typically IEEE-754 single precision
double d = 1.0; // typically IEEE-754 double precision
long double ld = 1.0L; // wider than double on many platforms, same width on MSVC
The standard only guarantees minimum widths, not exact ones — use <cstdint> when an exact width matters.
Fixed-Width Integers
#include <cstdint>
#include <cstddef> // std::size_t, std::ptrdiff_t
#include <cassert>
int32_t exact32 = -1; // exactly 32 bits, if the platform can provide it
uint64_t exact64 = 1; // exactly 64 bits, unsigned
int_least16_t atLeast16 = 1; // at least 16 bits, smallest such type
int_fast32_t fast32 = 1; // at least 32 bits, fastest such type on this platform
std::size_t sz = sizeof(int); // unsigned, wide enough to hold the size of any object
std::ptrdiff_t diff = 0; // signed, the type of pointer subtraction
int main() {
assert(sizeof(int32_t) == 4);
return 0;
}
Character Types
C++ has seven character types. Five of them carry a distinct encoding role and are the ones you reach for when
handling text — signed char and unsigned char are also character types formally, but are normally used as
small integers or raw bytes instead (std::byte is the better choice for the latter):
char c = 'a'; // the "narrow" execution character type
wchar_t wc = L'a'; // wide, platform-defined width (16-bit on Windows, 32-bit on Linux) -- avoid in new code
char8_t u8c = u8'a'; // UTF-8 code unit (C++20)
char16_t u16c = u'a'; // UTF-16 code unit
char32_t u32c = U'a'; // UTF-32 code unit / Unicode code point
char8_t is a distinct type from char specifically so UTF-8 strings and byte buffers are no longer
overload-ambiguous with each other — see Strings and Text.
bool
bool holds only true/false; every scalar type converts to bool (zero/null is false, anything else is
true), which is why if (ptr) and if (count) both compile even though neither is a bool.
auto and decltype
auto deduces a variable’s type from its initializer, following the same rules as template argument deduction:
auto i = 42; // int
auto d = 3.14; // double
auto& ref = i; // int&
const auto& cref = i; // const int&
auto* ptr = &i; // int*
std::vector<int> makeVector();
auto v = makeVector(); // std::vector<int> -- avoids repeating (or getting wrong) a long type name
decltype(expr) yields the declared type of expr without evaluating it, preserving references and
cv-qualifiers exactly — useful in generic code where auto alone would strip them:
int x = 0;
int& xr = x;
decltype(xr) xr2 = x; // int& -- decltype preserves the reference; auto would have deduced int
decltype(auto) forwardResult(int& r) { // return exactly what the expression's type is
return r; // int& -- decltype(auto) here deduces int&, not int
}
Type Aliases and Alias Templates
using (preferred) or the C-style typedef names an existing type; a using alias can also be a template,
which typedef cannot express:
using Distance = double;
using Callback = void(*)(int);
template <typename T>
using Vec = std::vector<T>; // alias template
Vec<int> numbers = {1, 2, 3}; // same as std::vector<int>
Implicit and Explicit Conversions
Arithmetic types convert implicitly (int to double, narrower to wider integer), which can silently lose
information going the other way:
double d = 3.9;
int truncated = d; // implicit narrowing conversion: 3 (fractional part discarded), often warned on
int big = 300;
char narrowed = big; // implementation-defined/UB-adjacent: value doesn't fit in char
struct Meters {
explicit Meters(double value) : value(value) {} // "explicit" blocks implicit conversion from double
double value;
};
explicit on constructors and conversion operators (covered fully in
Operator Overloading and Conversions)
is the main tool for opting a user-defined type out of C++'s permissive implicit-conversion rules. Brace
initialization (int x{3.9};) is stricter still: it is ill-formed for narrowing conversions, catching the
double-to-int example above at compile time instead of silently truncating.
<limits>, sizeof, and alignof
#include <limits>
#include <iostream>
int main() {
std::cout << std::numeric_limits<int>::max() << '\n';
std::cout << std::numeric_limits<int>::min() << '\n';
std::cout << std::numeric_limits<double>::epsilon() << '\n';
std::cout << sizeof(int) << '\n'; // size in bytes
std::cout << alignof(int) << '\n'; // required alignment in bytes
}
std::numeric_limits<T> is the type-generic, constexpr-usable replacement for the C macros INT_MAX/DBL_EPSILON.
See Also
-
Numbers and Math — floating-point pitfalls and
<cmath>. -
Constants, Enumerations, and Initialization —
const/constexprand initialization syntax. -
C: Basic Types and Values — the shared arithmetic and conversion model, without
auto/decltype, references, or C++'s stricter conversions.