Lexical Structure and Style
|
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. |
Tokens and Identifiers
Source text is broken into tokens: identifiers, keywords, literals, operators/punctuators, and comments (which are removed before tokenizing). An identifier is a sequence of letters, digits, and underscores not starting with a digit; C++ identifiers may also contain Unicode characters via universal character names.
Names starting with an underscore followed by an uppercase letter (_Foo), or containing a double underscore
(foo__bar), and any name starting with an underscore at global scope, are reserved to the implementation — never define your own names in that space, even though the compiler will not always diagnose it.
Keywords
C++23 has around 95 reserved keywords (int, class, template, constexpr, co_await, requires,
concept, …) that cannot be used as identifiers. <version> and feature-test macros (see
Namespaces, Modules, and the
Preprocessor) let code detect which contextual keywords/features a given compiler supports.
Literals
int decimal = 42;
int octal = 052; // leading 0 -- octal
int hex = 0x2A; // 0x -- hexadecimal
int binary = 0b0010'1010; // 0b -- binary; digit separators (') improve readability
long long big = 42LL;
unsigned u = 42U;
double d1 = 3.14;
double d2 = 6.02e23; // scientific notation
float f = 3.14f;
char c = 'A';
char8_t u8c = u8'A';
char16_t u16c = u'A';
char32_t u32c = U'A';
const char* s = "hello";
const char8_t* u8s = u8"hello";
const wchar_t* ws = L"hello";
bool flag = true;
std::nullptr_t np = nullptr;
Raw String Literals
A raw string literal (R"(…)") suppresses escape-sequence processing — ideal for regular expressions, file
paths, and embedded code:
#include <iostream>
int main() {
const char* path = R"(C:\Users\name\file.txt)"; // no need to escape backslashes
const char* regexPattern = R"(\d{3}-\d{4})";
std::cout << path << '\n' << regexPattern << '\n';
}
An optional delimiter (R"delim(…)delim") lets the literal contain the sequence )" itself.
User-Defined Literals
A user-defined literal (UDL) attaches a suffix to a literal, calling a function you define:
#include <iostream>
#include <chrono>
constexpr long double operator""_km(long double value) {
return value * 1000.0L; // kilometers to meters
}
int main() {
long double distance = 5.0_km;
std::cout << distance << '\n'; // 5000
std::cout << std::chrono::seconds(3).count() << '\n'; // library UDLs: 3s, 3min, 3h also exist via <chrono>
}
Standard library UDLs like 3s/3min (<chrono>), "text"s (std::string), and "pattern"sv
(std::string_view) live in inline namespaces such as std::literals::chrono_literals — see
Strings and Text and
Dates, Times, and Chrono.
Comments
// a single-line comment, to end of line
/* a block comment,
spanning multiple lines */
/// a documentation comment (Doxygen-style, convention only -- not part of the standard)
int square(int x);
Attributes
Standard attributes, written [[name]], annotate a declaration or statement for the compiler without
changing its meaning:
[[nodiscard]] int computeChecksum(const std::string& data); // warn if the return value is discarded
[[deprecated("use computeChecksum instead")]]
int checksum(const std::string& data);
void handle(int status) {
switch (status) {
case 0:
[[fallthrough]]; // silences "missing break" warnings intentionally
case 1:
break;
default:
[[unlikely]] throw std::runtime_error("bad status");
}
}
Naming and Formatting Conventions
The C++ Core Guidelines do not mandate one house style, but the ecosystem has converged on common patterns worth following for consistency with most open-source code and with the standard library itself:
-
Types:
PascalCase(project-specific) orsnake_case(matching standard-library style, e.g.unique_ptr). -
Functions and variables:
camelCaseorsnake_case, consistently within a codebase. -
Constants and enumerators:
kConstantNameorALL_CAPS, depending on house style. -
Macros:
ALL_CAPSalways — macros have no scope, so an unmistakable name reduces accidental collisions. -
Private/implementation-only members: a trailing or leading underscore (
count_or_count) by convention, never both, and never the reserved patterns from Tokens and Identifiers above.
clang-format (see Build and Tooling) automates formatting
so style debates do not repeat per pull request.
See Also
-
C: Lexical Structure and Style — the same token grammar; C23 took
[[attributes]]and'digit separators from C++.