A C++ acronym glossary
Someone on Slack recently noted C++’s penchant for cryptic acronyms. So I thought I’d write down a glossary of the ones I’m aware of. I’m sure I forgot some. If you think of one I’ve missed, please let me know! (And no, I’m not adding DesDeMovA.)
Cppreference also has a page of acronyms.
AAA
“Almost Always Auto.” A phrase, and coding style, introduced by Herb Sutter in Guru of the Week #94 (June 2013). It means writing
auto dx = x1 - x2;
auto p = std::make_unique<int>(42);
auto i = 0;
instead of
double dx = x1 - x2;
std::unique_ptr<int> p = std::make_unique<int>(42);
int i = 0;
respectively. (See also “The Knightmare of Initialization in C++” (2019-02-18).)
ABC
“Abstract base class.” That is, a class with at least one pure virtual function, intended for use as the root of a class hierarchy in classical OOP.
ABI, API
“Application Binary Interface” and “Application Programming Interface,” respectively. The API of a library
is the interface you program against, in a more or less high-level language. This could be as general as
saying “My library provides a Python API” (that is, to interface with my library, you’ll use Python); or
as specific as saying “std::map<K, V>::operator[]
accepts a parameter of type K
.” The API of a library
describes its interface in terms relevant to the human programmer.
Bob Steagall says an API is “a precise and complete specification of [a component’s] guaranteed user-visible behavior.” Visible to which user? The human programmer.
Louis Dionne says, “I like to think of ABI as being
like API, but for machine code.” The ABI of a library describes its interface in terms relevant to the
machine. For example, “Symbol
_ZNSt3mapI1K1VSt4lessIS0_ESaISt4pairIKS0_S1_EEEixERS5_
identifies a function that expects to be passed
the address of a map
object in %rdi
and the address of a K
object in %rsi
. It returns the address
of a V
object in %rax
.”
-
If you change the fundamental ideas behind your library, then your users may have to re-design their whole system architecture.
-
If you leave your library’s fundamentals alone but change its API, then your users won’t have to touch their system architecture; but they may have to make changes to their programs (i.e., re-translate their programs into C++ source code).
-
If you leave your library’s API alone but change its ABI, then your users won’t have to touch their C++ source code; but they may have to recompile everything (i.e., re-translate their programs from C++ into machine code).
An example of an ABI break that is not an API break would be if you changed one of the
function signatures in your library from
Item getItem(int index)
to Item getItem(const int& index)
.
At the C++ API level, these functions are called using exactly the same syntax.
Yet the ABI of the former is “pass index
in register %rdi
”; the ABI of the latter is
“pass the address of index
in %rdi
.” If you linked together two object files, one compiled
with the old ABI and one with the new ABI, they wouldn’t work together — you’d probably get a segfault.
Vice versa, an example of an API break that is not an ABI break would be if you changed
extern "C" Item getItem(const int& index)
to extern "C" Item getItem(const int *index)
.
These functions have the same ABI — they expect the address of an int
in %rdi
— but at the C++ API
level they’re called with different syntax. Anyone using the old API would have to modify
their C++ code — change getItem(i)
to getItem(&i)
— in order to compile it with the new API.
When we talk about “the Itanium C++ ABI” or “the MSVC ABI,” we’re talking more broadly about the collection of rules and relationships that go into defining the ABI of any C++ code — for example, the rules for name-mangling, for parameter-passing, for class layout and vtable layout, and so on. Two compilers that adhere to the same ABI (in this sense) can take API-compatible C++ source files and produce object files that are ABI-compatible (in the previous sense).
ADL
“Argument-dependent lookup.” See “What is ADL?” (2019-04-26).
ADT
“Abstract data type”; that is, any class type with which the user interacts only via a
high-level (“abstract”) interface. In C++, thanks to linguistic interference
from other meanings of the word “abstract,” you might see “ADT” used specifically to refer to
STL-style class templates, such as std::priority_queue
; that is, any class template which
is parameterized (“abstracted”) over an open set of parameter types.
All ADTs are also user-defined types (UDTs). Due to confusion over whether library types such as
std::string
were or were not “user-defined,” the paper standard has mostly stopped using the term
“UDT” in favor of “program-defined type.”
std::string
is not a “program-defined type.”
ARM
The Annotated C++ Reference Manual (Ellis and Stroustrup, 1990). This work — vastly outdated as of the mid-’90s, of course — consists of a reference manual for pre-standard C++, plus annotations and commentary by Stroustrup which “discuss what is not included in the language, why certain features are defined as they are, and how one might implement particular features.”
Confusingly for C++ programmers, ARM (originally “Acorn RISC Machine”) is also the name of a processor architecture used by many embedded devices, including most Android smartphones. Your C++ compiler might produce code to run on ARM, but your C++ compiler almost certainly does not accept the dialect of C++ described by the ARM!
BMI, CMI
“Binary Module Interface.” Just as .cpp files are compiled into .o files, and some compilers provide
ways to “pre-compile” .h files into PCHes, compilers that support C++20 Modules will have to provide
some way to compile C++ modules into some format that is precompiled, perhaps binary, perhaps compressed,
to make import
statements quick to compile.
The term “BMI” is not used by the paper standard. There is no standard BMI format. Each vendor will have their own format, just like they do today for precompiled headers and object files (although some of those formats may be governed by other standards, such as ELF). P0822 “C++ Modules are a Tooling Opportunity” (Gaby Dos Reis, October 2017) gives a very high-level sketch of the BMI format that Microsoft calls “IFC,” and which is modeled on something else called “Internal Program Representation” (IPR).
Because BMIs are not necessarily “binary” (in the sense of being highly compressed), GCC calls them “Compiled Module Interfaces” (CMI); and as of October 2020, the as-yet-unpublished “Modules Ecosystem Technical Report” (or “METeR”) glosses “BMI” as “Built Module Interface.”
BMI files are not a distribution format. When you distribute a module, you’ll be distributing its source code (as one or more files maybe with the extension .mpp). You won’t distribute the BMI file (extension .ifc) that MSVC produces, any more than you’d distribute the object file (extension .obj) that MSVC produces — in fact the urge to distribute .ifc files should be even rarer than the urge to distribute .obj files. Probably the best analogy really is to .pch files — one of the things C++20 Modules aim to replace.
CAS
Almost always stands for “compare and swap,”
a primitive atomic operation. The C++ standard library consistently calls this operation
compare_exchange
,
and provides _strong
(no-spurious-failure) and _weak
(spurious-failure-possible) versions.
Sometimes you might see “CAS” used to mean “copy and swap,” a C++ idiom that implements copy-assignment in terms of copy-constructing and then swapping. Compared to “open-coding” your assignment operator, the copy-and-swap idiom provides a simple, mechanical way to achieve both correctness and the strong exception guarantee. Some people say it’s overhyped or overused. Personally, I recommend it — and I never abbreviate it!
In specialized contexts, you might see “CAS” used to mean “content-addressed storage,” also known as “content-addressable memory” (CAM) or “associative storage.”
CNTTP
“Class non-type template parameter” (see NTTP). Since C++20 (specifically since P0732 and P1907), NTTPs are allowed to be of class type, as long as that type is structural. A structural type is simply a class type all of whose non-static data members are public and (recursively) structural — basically, any C-style struct. Thus:
template<int N> void f(); // OK
template<std::pair<int, int> NN> void g(); // OK since C++20
template<std::string S> void h(); // Error
When such a template is instantiated, the CNTTP’s “value” (that is, the values of its data members, recursively) is mangled into the instantiation’s linker symbol. On the Itanium ABI, that looks like this.
struct A { bool one; float two; };
constexpr A x = {true, 1.0};
constexpr A y = {false, 3.14};
template<A Value> float foo() { return Value.two; }
int main() {
foo<x>(); // call _Z3fooIXtl1ALb1ELf3f800000EEEEfv
foo<y>(); // call _Z3fooIXtl1ALb0ELf4048f5c3EEEEfv
}
Why
3f800000
and4048f5c3
? See “Bit patterns offloat
” (2021-09-05).
Inside a function taking a CNTTP, the value of the template parameter is accessible
as a template parameter object,
which is more or less an inline constexpr
variable shared by everyone in the program.
(Godbolt.)
Note that the type A
might be non-comparable (as here), or even define its operator==
such that x == y
.
Still, foo<x>
and foo<y>
will be distinct instantiations because the “structural” value representations of
x
and y
differ, regardless of what operator==
says. Back in 2019, I disliked this so much that I
wrote a paper proposing to remove
CNTTPs from C++20. WG21 didn’t buy it.
CPO
“Customization point object.” This is a notion introduced by Eric Niebler’s Ranges library, which means it’s new in C++20.
A customization point object is a function object with a literal class type that interacts with program-defined types while enforcing semantic requirements on that interaction. —N4810 [customization.point.object]/1
That is, a CPO is an object (not a function); it’s callable; it’s constexpr-constructible (that’s what “literal” means in this context); it’s customizable (that’s what it means to “interact with program-defined types”); and it’s concept-constrained.
(WG21 has a fetish for describing concept constraints as “semantic requirements,”
even though C++20 Concepts are a purely syntactic feature because
nobody knows how to specify semantic requirements.
The compiler can ensure that some T
provides syntax for both ==
and !=
, but it won’t check their semantics.)
In practice, this looks more or less like
namespace detail {
template<class A, class B>
void swap_helper(A& a, B& b) {
using std::swap;
swap(a, b);
}
}
inline constexpr auto swap =
[]<A, B>(A& a, B& b)
requires Swappable<A> && Swappable<B>
{
return detail::swap_helper(a, b);
};
(The C++20 standard has a lot of wording inherited from Eric’s Ranges-v3 to deal with
something colloquially known as the “poison pill”; but I observed, and Eric confirmed,
that the poison pill hasn’t been necessary ever since C++17 introduced a SFINAE-friendly std::swap
.)
The benefit of a CPO over a named function is that it separates the two pieces of the customization point:
-
A, the piece the user is required to specialize; and
-
B, the piece the user is required to invoke (and therefore must not specialize).
In the above example, “A” would be your ADL overload of swap
, and “B” would be the swap
CPO itself.
Also, when you call a CPO, even if you don’t qualify its name, you don’t get ADL — which means you get more predictable behavior.
So, to recap, a CPO is a “callable, constexpr-constructible, customizable, concept-constrained object.” Maybe it should have been called a “C6O” instead!
If you remove the adjectives “customizable, concept-constrained” from the above, then you have a
function object that turns off ADL — but is not necessarily a customization point.
The C++20 Ranges algorithms, such as std::ranges::find
,
are like this. Any callable, constexpr-constructible
object is colloquially known as a “niebloid,” in honor of Eric Niebler.
A CPO is simply a niebloid that wraps a user-definable customization point.
CRTP
The “Curiously Recurring Template Pattern.” See Wikipedia. Occasionally misquoted as “Curiously Recursive.”
CTAD
“Class template argument deduction.” See cppreference. Occasionally misquoted as “Constructor template argument deduction,” even in the WG21 papers that originally added the feature. (It’s much easier to remember the acronym “CTAD” than to remember what the feature does! It deduces the template arguments to the class template. The arguments to any particular constructor template are deduced, as always, via template argument deduction.)
CTRE
“Compile-time regular expressions.” Specifically,
Hana Dusíková’s ctre
library,
which allows you to write things like
#include <ctre.hpp>
static_assert(ctre::match<"^h.*[wxyz]orl[^y]">("hello world"));
The current version of CTRE relies on C++20’s class-type NTTPs. Before C++20, it relied on a compiler extension supported by GCC 8.x and Clang 5–11 (but no longer by either compiler’s trunk):
using namespace ctre::literals;
static_assert("^h.*[wxyz]orl[^y]"_ctre.match("hello world"));
CWG, EWG, EWGI, LEWG, LEWGI, LWG
These are the main working groups of the ISO C++ Committee (a.k.a. WG21). At least in theory, their responsibilities are as follows:
-
The Evolution Working Group Incubator (EWGI, pronounced “oogie”; a.k.a. SG17) evaluates proposals for core-language features.
-
The Evolution Working Group (EWG) designs core-language features.
-
The Core Working Group (CWG) reviews core-language wording.
-
The Library Evolution Working Group Incubator (LEWGI, pronounced “lewgie”; a.k.a. SG18) evaluates proposals for standard library facilities.
-
The Library Evolution Working Group (LEWG) designs standard library facilities.
-
The Library Working Group (LWG) reviews standard library wording.
EWGI and LEWGI are very new in the grand scheme of things; they met for the first time at the San Diego meeting (November 2018).
By the way, “ISO WG21” stands for Working Group 21 of the International Organization for Standardization; and “SG17” means “Study Group 17.” For a list of study groups, see isocpp.org.
When you see “CWG” or “LWG” followed by a number, as in “CWG1430” or “LWG3237,” it’s referring to an issue on CWG’s or LWG’s plate — an open question raised by the wording of the Standard. LWG’s FAQ gives an exhaustive list of states an issue can be in, including resolved states such as “DR” and “NAD” (Not A Defect).
D&E
The Design and Evolution of C++, a book by Bjarne Stroustrup first published in 1994. In Stroustrup’s words, “D&E discusses why C++ is the way it is. It describes the design of C++. The emphasis is on the overall design goals, practical constraints, and people that shaped C++.”
DR
“Defect Report.” This means a defect or open question raised by the wording of the Standard, which has been discussed and prospectively resolved by CWG and/or LWG, resulting in an amendment or erratum to the Standard of a technical nature (as opposed to a merely editorial fixup). Formally, I believe the term “DR” refers to the question or problem, whereas the ultimately adopted solution is formally a “technical corrigendum” (TC). In common parlance, you’ll hear “ah, that issue was resolved by DR 409” — “DR 409” being a colloquial shorthand for “the resolution of the DR arising from LWG issue 409,” and it is only from context that we can tell it was LWG 409, not CWG 409. Defect Reports themselves are not numbered, per se.
DRs are often applied retroactively. For example,
N3922 “New Rules for auto deduction from braced-init-list” (James Dennett, February 2014),
being associated with an (unnumbered) DR,
was not merely adopted into the C++17 working draft, but also retroactively applied
to the already-published C++11 and C++14 standards.
This manifests as a difference in -std=c++11
behavior between Clang 3.5.1
(shipped January 2015) and Clang 3.8 (shipped March 2016) — not as a difference between -std=c++11
and
-std=c++17
on any compiler! So in this sense, to “resolve foo as a DR” connotes “to apply the same fix
uniformly across all language modes.”
EBO, EBCO
“Empty Base (Class) Optimization.” This is the thing that, given the code
struct A {};
struct B { int i; };
struct C : public A { int i; };
makes B
and C
have the same size
(and in fact the same layout).
EDG
Edison Design Group, a small company that makes compiler front-ends. Its C++ front-end was first released in 1992. Essentially all of its employees are extremely core members of the C++ standardization committee (WG21).
EH, TDEH
“Exception Handling.” See also: SEH.
When talking about standard C++ exception handling, you may see references to “setjmp
/longjmp
exception handling” versus “table-driven exception handling” (TDEH). The former is the old-school
implementation used in the code generators for compilers like Cfront (source):
opening a new scope essentially calls setjmp
to build a dynamic stack of “things that need to be
unwound when we throw,” and then throw
essentially calls longjmp
as many times as it needs in
order to unwind the stack back to the appropriate handler.
TDEH has pretty much taken over the world in the past two decades. Opening a new scope in TDEH is
a free operation; for this reason it’s also been colloquially called “zero-cost exceptions.”
throw
essentially consults a static data table of “things that need to be unwound when we throw
from this particular stack frame,” and then unwinds one stack frame, and repeats, as many times as
it needs in order to unwind the stack back to the appropriate handler. TDEH pays a relatively larger
up-front cost in data size; setjmp
/longjmp
exception handling pays a relatively larger runtime cost
and also a larger cost in code size.
EoP, EOP
Another overloaded acronym with at least two obscure meanings.
The original meaning of “EoP” is Elements of Programming (2009), a book by Alexander Stepanov (of STL fame) and Paul McJones.
In C++23, I’ve seen “EOP” used as an abbreviation for “explicit object parameter.” This is the new-in-C++23 feature that lets you write
struct S {
int x;
int f(this S self) { return self.x; }
};
We might say that self
is an explicit object parameter, or that S::f
is an explicit object parameter function. (MSVC calls it an “explicit object
member function,” without the word “parameter.”) Personally, I don’t recommend
abbreviating this expression, yet; but we’ll see how usage evolves.
For more on explicit object parameters, see the paper that introduced them
into C++23: P0847 “Deducing this
”
(Gašper Ažman et al., July 2021).
FAM
“Flexible array member.” This is the C99 feature that lets you write
struct S {
int x, y, z;
char extra_space[];
};
struct S *ps = malloc(sizeof(S) + 10);
strcpy(ps->extra_space, "some data");
The “flexible” member must have no array bound, and must appear as the last member of the struct.
Flexible array members are not part of C++, and likely never will be, officially.
Accessing off the end of an object will always technically be undefined behavior.
Nevertheless, C++20’s destroying delete
facility was designed specifically to support FAM-like techniques.
GCC
Originally the “GNU C Compiler” (where “GNU” famously stands for “GNU’s Not Unix”). Since 1999 (source), the acronym has stood for “GNU Compiler Collection.” One of the big three C++ compiler vendors, besides Clang and MSVC.
GMF, PMF
“Global module fragment” and “private module fragment.” In C++20 Modules syntax, the global module
fragment is the portion of a module unit (that is, a TU containing a module-declaration
such as module hello;
) which appears at the very top of the source code, preceded by the line
module;
and followed by the module-declaration.
The private module fragment appears at the very bottom, preceded by the line
module :private;
. Things in the PMF are not export
ed; you can modify things in module
hello
’s PMF without needing to recompile other TUs that import hello
.
module;
#include <unistd.h> // this is part of the GMF
extern int gmf(); // provided by some non-modules third-party code
module hello;
export void pmf();
export void foo() { gmf(); pmf(); }
module :private;
void pmf() { } // this is part of the PMF
I don’t fully understand why the PMF is needed; it seems like we could have put the definition of
void pmf()
up in the module, and simply refrained from marking that definition as export
.
But perhaps I’m missing something. Anyway, for more on module fragments, see vector<bool>
’s blog post
“Understanding C++ Modules, Part 3”
(October 2019).
“PMF” also happens to be an acronym for “pointer to member function,” as in the
expression &Foo::setValue
or the type void (Foo::*)(int)
.
HALO
“Heap Allocation eLision Optimization.” This is the optimization on C++20 coroutines
referred to in Gor Nishanov’s talk
on the “disappearing coroutine” (CppCon 2016).
See “Announcing Quuxplusone/coro
” (2019-07-03),
specifically this example; see also
P0981 “HALO: the joint response”
(Richard Smith & Gor Nishanov, March 2018).
Normally, each time you enter at the top of a C++20 coroutine and create its return object
(regardless of whether you’re multi-threading)
you’ll have to heap-allocate enough space to store the coroutine’s stack frame. However, in
some specific cases where the compiler can statically determine the lifetime of the coroutine
frame — determine that it will never “escape” from a very localized region of the code — then
the compiler can find a place higher up on the stack to allocate space for it. In that case,
the heap-allocation becomes unnecessary and can be “elided.” This can happen quite often in the
generator/co_yield
use-case, if your generator type is carefully crafted.
My understanding is that HALO will basically never happen in the multi-threaded/co_await
use-case.
Even when the heap-allocation cannot be elided, C++20 std::coroutine_traits
provides
rudimentary hooks for the programmer to customize the heap allocation mechanism. See
“C++ Coroutines: Understanding the promise type”
(Lewis Baker, September 2018).
ICE
“Internal compiler error.” A compiler (for C++ or any other language) should always be able to compile your code or else tell you what’s wrong with it. If you give the compiler some input and it gets so confused that the compiler itself assert-fails, then you’ve discovered an internal compiler error. Many compilers’ assertion-failure messages actually contain the words “internal compiler error” (for example, GCC). I don’t know the original inventor of the term.
If the compiler segfaults or dies from an unhandled exception, you could reasonably call that an “internal compiler error” too. Some compilers will install signal handlers or exception handlers to turn such bugs into assertion failures that actually print “internal compiler error”; some won’t.
Sadly for clarity of communication, “ICE” is also the initialism for “integral constant expression.”
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression. [Note: Such expressions may be used as bit-field lengths, as enumerator initializers if the underlying type is not fixed, and as alignments. —end note] —N4810 [expr.const]/5
IFNDR
“Ill-formed, no diagnostic required.” To a first approximation, this means the exact same thing as “undefined behavior” (UB). Specifically,
If a program contains a violation of a rule for which no diagnostic is required, this document places no requirement on implementations with respect to that program. —N4810 [intro.compliance]/2.3
The standard sometimes uses the phrase exactly (e.g. [dcl.attr.noreturn]/1), and sometimes uses variations (e.g. [using.headers]/3):
A translation unit shall include a header only outside of any declaration or definition, and shall include the header lexically before the first reference in that translation unit to any of the entities declared in that header. No diagnostic is required.
What this means, implicitly, is that if the preceding “shall” statement is violated by the user’s program, no diagnostic is required. So you’ll have an ill-formed program (which by definition is “not C++”). Because no diagnostic is required, your C++ compiler is not required to tell you that its input was “not C++”; but, because the input wasn’t C++, your C++ compiler is not required to produce any particular output. It might produce nasal demons. It might even ICE, although that wouldn’t be nice.
Frequently, the distinction between “IFNDR” and “UB” is that “IFNDR” connotes a static property of the code, whereas “UB” connotes a runtime property. A division routine can have conditional UB at runtime (if you pass it a divisor of zero), but code cannot be conditionally IFNDR at runtime. Vice versa, if some mistake (such as an ODR violation) could conceivably result in a linker error, then the mistake will typically be described in terms of “IFNDR” rather than “UB.” Recall that if a program is ill-formed, then the compiler doesn’t have to generate code for it; but if a program is well-formed, then the compiler must generate some sort of code for it, even in the presence of undefined behavior.
IILE
“Immediately invoked lambda expression.” This is a relatively obscure idiom in C++, but we plucked it from JavaScript, where it is called “immediately invoked function expression” (IIFE). In JavaScript, IIFEs are typically used to avoid polluting the current scope with helper variables — which is important because in JavaScript you’re often working in the global scope. In C++, the idiom is typically used to avoid mutation in the current scope. That is, rather than write an initialization as
void test(int *first, int *last, bool(*pred)()) {
std::vector<int> v(first, last);
std::sort(v.begin(), v.end());
std::stable_partition(v.begin(), v.end(), pred);
// Now do several things that don't involve modifying `v`
}
you might move the initial sorting-and-partitioning into a helper function — which you make
a lambda so that it can use first
, last
, and pred
without cumbersome argument-passing,
and so that you don’t have to scroll around while reading the code. The end result:
void test() {
const std::vector<int> v = [&]() {
std::vector<int> v(first, last);
std::sort(v.begin(), v.end());
std::stable_partition(v.begin(), v.end(), is_prime);
return v;
}();
// Now do several things that don't involve modifying `v`
}
For a dangerous example of using IILEs with C++20 coroutines, see “C++2a Coroutines and dangling references” (2019-07-10).
The C++20 standard’s notion of “immediate invocation”
has absolutely nothing to do with IILEs; it has to do with the evaluation of C++20 consteval
functions.
IWYU
“Include What You Use.” This is both a slogan and the name of a specific libclang-based tool.
Basically, “Include What You Use” means that if you use an entity, you should explicitly #include
the header that is documented to provide that specific entity. Don’t rely on its being provided transitively
by including any other header, because what’s true today might be false tomorrow.
(See also: Hyrum’s Law.)
#include <vector>
std::vector<std::unique_ptr<int>> v; // IWYU violation
Since std::vector
depends on std::allocator
, which is defined in <memory>
, you might expect that
<vector>
needs to include <memory>
— and indeed on libc++ 12.0 it does. But on libstdc++, MSVC, and
libc++-sometime-in-the-future (as of July 2021), this TU fails to compile.
To fix it, you should “Include What You Use”:
#include <memory> // for unique_ptr
#include <vector> // for vector
std::vector<std::unique_ptr<int>> v;
The include-what-you-use
command-line tool (on OSX, simply brew install include-what-you-use
) can detect
and even suggest how to correct IWYU violations. Its output is usually pretty self-explanatory:
$ include-what-you-use test.cpp
test.cpp should add these lines:
#include <memory> // for unique_ptr
test.cpp should remove these lines:
The full include-list for test.cpp:
#include <memory> // for unique_ptr
#include <vector> // for vector
---
LTO
“Link-Time Optimization.” Any kind of optimization that requires looking at the whole program — thus also sometimes known as “whole-program optimization” (WPO) or “whole-program analysis” (WPA). This is a special case of “interprocedural optimization” (IPO).
LLVM’s docs have a great example showing how LTO can iteratively remove dead (but non-static) functions, and then update global invariants to cause even more code to go dead.
When I worked at Green Hills, their linker was known for its super aggressive link-time optimizations such as function outlining (i.e., the opposite of function inlining) and unused virtual function deletion (UVFD).
MDT
“Most Derived Type.” Also known as the “dynamic type,” or (in libsupc++) the “whole type,” this is the actual, real, true type of an OOP object, as opposed to the static type of the pointer through which you’re currently accessing it. For example, in
Fruit *p = new Apple;
the static type of *p
is Fruit
, but the dynamic type of that object — that object’s most derived type —
is Apple
.
MSVC
Microsoft Visual C++ — in C++ contexts, essentially a synonym for Microsoft Visual Studio (VS or MSVS).
MVP
Another overloaded acronym. Since it’s so overloaded, you probably won’t hear the initialism used without any context — and shouldn’t use it that way, either.
All the top Google hits are for “Model–View–Presenter,” a variation on Model–View–Controller (MVC).
Microsoft identifies “MVPs” (“Most Valuable Professionals”) in the community; the list as of 2021 includes such luminaries as Kate Gregory, Jon Kalb, and Jason Turner.
Of course in baseball “MVP” stands for “Most Valuable Player,” and in Silicon Valley it often stands for “Minimum Viable Product.”
But its most C++-specific meaning is “Most Vexing Parse.” The Most Vexing Parse affects old-school direct-initialization with parentheses, and goes like this:
const char *s = "foo.txt";
fs::path t(s); // OK: t is a variable
fs::path u = std::string(s); // OK: u is a variable
fs::path v(std::string(s)); // Yuck: v is a function!
fs::path w(std::string()); // Yuck: w is a function too!
v
and w
are perfectly valid C++ code. In v
, the compiler
ignores the “redundant” parentheses around s
and parses it
as equivalent to
fs::path v(std::string s); // v is a function
In w
, the compiler treats std::string()
as a function type —
the type of an unnamed function parameter — and decays it to produce
the equivalent of
fs::path w(std::string (*param)()); // w is a function
The simplest way to fix a Most Vexing Parse issue is to stop declaring things
with T x(y)
syntax, and use T x = y
or auto x = T(y)
instead.
See “The Knightmare of Initialization in C++” (2019-02-18).
auto v = fs::path(std::string(s)); // OK and best
auto w = fs::path(std::string()); // Also works for w
Sillier alternatives, which satisfy the parser without improving readability for the programmer, are to replace the outer parentheses with curly braces, or to double them up, or to use a cast operator:
fs::path v{std::string(s)}; // OK but not best
fs::path v((std::string(s)));
fs::path v(static_cast<std::string>(s));
NSDMI
“Non-static data member initializer.” This is the C++11 feature that allows you to write “initializers” on the member variables of a class.
struct S {
static int s = 1; // an initialized static data member
int x; // a non-static data member, sans initializer
int y = 2; // NSDMI!
};
This term gained currency in 2019 because of Corentin Jabot’s proposal for
“auto
NSDMIs” — non-static data members
whose type is deduced (at class-definition time) from the type of their initializer.
auto
NSDMIs are not (yet?) part of any draft standard.
NTBS
“Null-Terminated Byte String.” Not to be confused with the NTSB.
Most C and C++ functions that take a parameter of type const char *
expect that it points to
the first character of an NTBS. "hello world"
is an example of an NTBS.
Here, “null-terminated” means it ends with a '\0'
byte (ASCII NUL), and “byte string” just means that
we’re ignoring all encoding-related nonsense.
Cppreference distinguishes NTBS from NTMBS
(“null-terminated multibyte string,” e.g., UTF8-encoded) and even
NTWS (“null-terminated wide string”,
i.e., a sequence of wchar_t
s ending with wchar_t(0)
).
NTTP
“Non-type template parameter.” This is a weird one, because you’d think by symmetry it ought to be spelled “TNTP” — template type parameter, template template parameter, template non-type parameter, right? But no: C++ has “template type parameters” and “non-type template parameters.”
A template type parameter is like template<class C>
.
A template template parameter is like template<template<class> class TC>
.
A non-type template parameter is like template<int V>
or template<auto V>
.
Until C++20, NTTPs were allowed to be of only certain primitive types: integers, enums, pointers, and references.
Starting in C++20, NTTPs are allowed to be of floating-point types (as in MyTemplate<3.14>
) or
even certain kinds of class types; see CNTTP.
NUA
[[no_unique_address]]
, an attribute that is new in C++20. I would never abbreviate it myself,
but I’m starting to notice people using “NUA” in Slack, so, into the glossary it goes! Not to be
confused with NUMA.
C++20 introduced [[no_unique_address]]
basically to get rid of the need for the EBO.
NVI
“Non-virtual interface.” This rare acronym refers to the increasingly common (and, in my view, good) practice of separating the two pieces of the customization point even for plain old classical polymorphism. The piece specialized by the derived class stays as a virtual function (but becomes private); the piece invoked by the caller stays public (but becomes non-virtual).
class AbstractWidget {
virtual void do_frobnicate() = 0;
public:
void frobnicate() { this->do_frobnicate(); }
};
class DerivedWidget : public AbstractWidget {
// note: implicitly "private"
void do_frobnicate() override { ... }
}
The Non-Virtual Interface pattern is used in <iostream>
(public non-virtual sputn
and protected virtual xsputn
),
and also in PMR
(public non-virtual allocate
and private virtual do_allocate
).
ODR
The “One-Definition Rule.” See cppreference. The ODR is quite long and involved — it takes up four printed pages in the paper standard! But the important points are:
Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required. —N4810 [basic.def.odr]/10
An odr-use, to a first approximation, is any use that requires the used entity to be defined somewhere.
(This excludes things like asking for the sizeof
or decltype
of a variable.)
By “discarded statement,” they mean the untaken branch of an if constexpr
.
There can be more than one definition of a class type, enumeration type, inline function with external linkage, inline variable with external linkage, class template, non-static function template, concept, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified in a program provided that […] the definitions satisfy the following requirements. […]
- each definition of D shall consist of the same sequence of tokens
—N4810 [basic.def.odr]/12
The first quote above disallows programs like
// alpha.cpp
int i = 0;
// beta.cpp
int i = 0;
The second quote disallows programs like
// alpha.cpp
inline int foo() { return 0; }
// beta.cpp
inline int foo() { return 1; }
(but if you changed that 1
to a 0
, then the program would be well-formed).
Both of these programs exhibit “ODR violations.” A program which violates the ODR is IFNDR. In the first example above, you’ll likely get a linker error; in the second example, you’ll likely get a compiled program with unexpected runtime behavior.
OOP
“Object-oriented programming,” of course. Sometimes shortened even further to “OO,” as in “OO principles.”
PCH
“Pre-compiled header.”
Normally, when you #include "foo.h"
, you have to recursively open all
the files that it includes and parse all that C++ code (Include guards defend against doing this more
than once per translation unit, but if you have a hundred translation units, you’ll still be opening all
those files a hundred times.) Therefore, most compilers support some way to pre-compile “foo.h”
so that when you say #include "foo.h"
, the compiler actually just opens “foo.pch” — a preprocessed, parsed,
and compressed representation of all the code recursively included by “foo.h”.
Most compilers restrict the usage of PCH files — e.g. requiring that each translation unit can only include one PCH, and it must be the very first non-comment line in the file. And notably, from GCC’s docs:
The precompiled header file must have been produced by the same compiler binary as the current compilation is using.
That is, PCH files are not a distribution format. See also: BMI.
PGO
“Profile-Guided Optimization,” occasionally called “profile-driven optimization” (PDO). You compile your program with profiling instrumentation; then you run it through its paces to collect a profile; and then you feed that profile back into a second invocation of the compiler. From the profile, the compiler can tell what loops are hot, what functions are frequently called together, and so on; which can lead to better codegen the second time around.
This is a topic that I wish I knew more about.
PIMPL
“Pointer to IMPLementation.” Variously capitalized “PIMPL,” “PImpl,” or “pImpl,” this is a technique for moving expensive implementation details out of your most commonly traveled .h files and into separately compiled .cpp files. See cppreference.
PMR
“Polymorphic Memory Resources.” C++17 added the PMR library, mostly in the header <memory_resource>
,
and mostly in the nested namespace std::pmr
. The most important components are
std::pmr::memory_resource
, which is a traditional abstract base class; and
std::pmr::polymorphic_allocator<T>
, which is an allocator (similar to std::allocator<T>
) which
holds within itself a pointer to a memory_resource
that it uses to fulfill requests for memory.
For more on PMR, see ”<memory_resource>
for libc++” (2018-06-05)
and my talk “An Allocator is a Handle to a Heap”
(C++Now 2018, CppCon 2018).
POCCA, POCMA, POCS, SOCCC
“propagate_on_container_copy_assignment
,” “propagate_on_container_move_assignment
,”
“propagate_on_container_swap
,” and “select_on_container_copy_construction
,” respectively.
When you have an STL container (such as std::vector
) with a custom allocator type, you can write
A a1("foo");
A a2("bar");
assert(a1 != a2); // for the sake of argument
std::vector<int, A> v1(a1);
std::vector<int, A> v2(a2);
v1 = v2; // A
v1 = std::move(v2); // B
std::swap(v1, v2); // C
std::vector<int, A> v3(v1); // D
Before line A, we clearly have v1.get_allocator() == a1
. After line A, does v1.get_allocator()
equal
a1
or a2
? What about after line B? What about after line C? After line D, does v3.get_allocator()
equal a1
or A()
or something else?
The standard library’s std::allocator_traits<A>
exposes member typedefs named propagate_on_container_copy_assignment
, propagate_on_container_move_assignment
,
and propagate_on_container_swap
that control these behaviors; they’re inherited from the allocator type
A
if possible, or else defaulted to false_type
. If they’re all true_type
, then you have a traditional
allocator that fully enables “pilfering” the allocation from one vector into another (in cases B and C above).
If they’re all false_type
, then you have a “sticky” allocator that inhibits pilfering in cases where the
source and destination containers have different allocators. C++17’s PMR allocators are “sticky.”
If some of POCCA/POCMA/POCS are true_type
and some are false_type
for the same allocator type,
then you probably have a bug.
Notice that std::allocator_traits<A>::select_on_container_copy_construction(const A&)
is a static member function,
not a simple compile-time true_type
or false_type
. For example, PMR makes it return the runtime result of
std::pmr::get_default_resource()
. Also, there’s no select_on_container_move_construction
, because C++17 assumes
that move construction is always kind of “invisible” (indistinguishable from copy elision) as far as allocators are
concerned. (Read: PMR didn’t need this specific customization point, so it wasn’t included in the proposal.)
If your allocator type is stateless and/or sets is_always_equal
, then the settings of
POCCA/POCMA/POCS don’t really matter and might just as well be inconsistent.
For historical reasons,
std::allocator
falls into that category.
For more on this topic, see my talk “An Allocator is a Handle to a Heap” (C++Now 2018, CppCon 2018). I also covered allocators in my training course The STL From Scratch (CppCon 2017, 2018, 2019).
POD
“Plain Old Data.”
This term has been deprecated in C++20, along with the type trait std::is_pod<T>
.
PR
Most commonly, in software engineering, a “PR” is a “Pull Request” — either literally a GitHub pull request, or in the more general sense of a patch that’s ready for code review.
Confusingly, the Clang/LLVM project has historically used “PR” to mean “Problem Report,” so if you see a Clang or libc++ person talking about “PR12345,” they probably mean Bugzilla bug number 12345. (Clang/LLVM pull requests will be identified as “D123456,” where the D stands for “Differential”; this in turn is not to be confused with WG21’s use of “D1234” to identify a pre-publication draft version of a P-numbered paper.)
In CWG and LWG, on the other hand, “P/R” (with the slash) stands for the “Proposed Resolution” to an open issue — that is, what change are we going to make to the standard’s wording in order to close some loophole? This meaning is not that far different from “Pull Request,” but applies to standardese rather than code.
So a libc++ maintainer might say — with a reasonable expectation of being understood — “Implementing the P/R for LWG2357 will fix PR2468; I’ve done that in D123456. The same PR includes an implementation of D2581 from next month’s mailing.” Or: “I know you want this fixed, but I can’t submit a PR until someone suggests a P/R!”
QoI
“Quality of Implementation.” This is the C/C++ committee wonk’s version of “That’s a hardware problem.” The C++ Standard mandates certain behaviors of a conforming C++ implementation; but the Standard is generally silent on issues of usability, performance, debuggability, cross-platform portability, and so on.
Oh, your compiler ICEs when you try to compile
INT_MAX + 1
? That’s not a conformance issue, it’s a quality-of-implementation issue.
Bring it up with your compiler vendor.
If one compiler supports function types with up to 1024 parameters, and another compiler supports only up to 16 parameters, which one is “right”? Neither. Both. That’s a QoI issue.
In D&E, Bjarne Stroustrup gives the following (non-exhaustive) examples of QoI issues: vtable layout; name-mangling scheme; debuggability; the compiler’s behavior when confronted with an ODR violation; and the availability of a Boehm-style garbage collector. For other QoI issues, consult the Standard’s list of implementation-defined behaviors and list of suggested minimum implementation limits.
Not to be confused with the Klingon verb “Qol.”
RAII
“Resource Acquisition Is Initialization.” This is a brush capable of very broad strokes, but it boils down to the idea that you should have destructors that free your resources, copy constructors that duplicate your resources, and copy-assignment operators that do both. It’s as broad and vague a slogan as “move semantics” or “value semantics,” though; different people might express its fundamental precepts in slightly different ways.
Shameless plug: I’ll be giving a talk on “RAII and the Rule of Zero” at CppCon this September! Come see it!
It should really have been called Resource Freeing Is Destruction, but that acronym was taken.
RTTI
“Runtime Type Information.” This is the metadata that’s generated for each user-defined class type
for use by C++ runtime features such as dynamic_cast
, typeid
, and exception-handling.
Many compilers provide a command-line switch such as -fno-rtti
to limit or eliminate this
information (which of course limits or eliminates the programmer’s ability to use dynamic_cast
and typeid
).
For background on dynamic_cast
’s use of RTTI, see my talk
“dynamic_cast
from scratch” (CppCon 2017).
RVO, NRVO, URVO
“Return Value Optimization,” a.k.a. “copy elision” (or, in C++11 and later, “move elision” is also a thing).
There are two places where copy elision (a.k.a. RVO) typically kicks in. The place where C++17 mandates that it happen (thanks to “deferred materialization of temporaries,” a.k.a. “guaranteed copy elision”) is when the object being returned is a prvalue:
return x+1;
return Widget();
Since the temporary object here has no name, this is colloquially known as “unnamed return value optimization” (URVO).
The other place where copy elision might happen, but is optional, is when returning a local variable by name:
Widget x; [...]
return x;
Since this return value has a name, this is colloquially known as “named return value optimization” (NRVO).
Notice that “URVO” can be seen as a special case of guaranteed copy elision, which would also kick in if you wrote something like
void foo(Widget);
void test() {
foo(Widget());
}
Our Widget
is constructed directly into the parameter slot. This is arguably not “RVO”
since it doesn’t involve “return values” — it is not even “copy elision” according to the paper standard —
but it is definitely still “deferred materialization of temporaries.”
I am not aware of any way to trigger “NRVO” other than via a return
statement. It is
technically allowed in throw
statements as well, but
no compiler implements that feature.
It is also technically allowed in C++20 co_return
statements, but neither Clang nor MSVC
implement that feature. (MSVC doesn’t even do implicit move!)
For more on this topic, see my talk “RVO Is Harder Than It Looks” (CppCon 2018).
SBO, SOO, SSO
“Small Buffer Optimization,” referring to a small buffer held within the memory footprint of a type
that in the general case would have to dynamically allocate memory to hold something or other.
The small buffer is used to avoid that memory allocation in “small” cases. If our buffer is being
used to store an object, then we might say that our type has a “Small Object Optimization” (SOO).
If our buffer is being used to store a string, then we have a “Small String Optimization” (SSO).
For slightly more on SBO/SOO/SSO, see
“The space of design choices for std::function
” (2019-03-27).
SCARY iterators
This silly initialism was introduced in N2911 “Minimizing Dependencies within Generic Classes for Faster and Smaller Programs” (Tsafrir, Wisniewski, Bacon, Stroustrup; June 2009). It refers to the template-metaprogramming technique of keeping “policy parameters” such as allocators at the outermost possible level and not letting them pollute the lower levels of the system. It’s the difference between
template<class T, class A>
class vector {
struct iterator { ... };
};
// Many distinct iterator classes
static_assert(not std::is_same_v<
vector<int, A1>::iterator,
vector<int, A2>::iterator
>);
and
template<class T>
class vector_iterator { ... };
template<class T, class A>
class vector {
using iterator = vector_iterator<T>;
};
// Fewer iterator classes: hotter code
static_assert(std::is_same_v<
vector<int, A1>::iterator,
vector<int, A2>::iterator
>);
To quote the paper:
The acronym SCARY describes assignments and initializations that are Seemingly erroneous (appearing Constrained by conflicting generic parameters), but Actually work with the Right implementation (unconstrained bY the conflict due to minimized dependencies).
See also: “SCARY metafunctions” (2018-07-09).
For another example of an initialism that doesn’t come (entirely) from initials, see HALO.
SEH
Microsoft Windows has a feature called
Structured Exception Handling (SEH);
I don’t know much about it except that it somehow unifies C++ exception handling and things that
would be considered “signals” in POSIX-land, such as floating point exceptions. If you pass the
/EHc
flag to MSVC, or simply omit /EHsc
, then you can
catch division-by-zero as a C++ exception! This works only because
of SEH. There’s no equivalent on non-Windows operating systems as far as I know.
SFINAE
“Substitution Failure Is Not An Error.” This is the slogan that helps you remember why the compiler doesn’t complain about
template<class T>
void f(T, typename T::type) { puts("hello"); }
template<class T>
void f(T, long) { puts("world"); }
void test() {
f(1, 2); // "world"
}
Here, we’ve already done template argument deduction on our first candidate f
and figured out
that if we call this candidate, then we’ll set T=int
. The “failure” happens during template
argument substitution — when we try to extract a member type T::type
from T=int
, and find
that there is no such member. If we’d been compiling ordinary code and seen
using T = int;
void f(T, typename T::type) { puts("error"); }
then that would have been a hard error. But in this case, we merely have a substitution failure,
which is Not An Error. We simply discard this candidate f
and start looking at the next
candidate, which turns out to work fine. This is exactly the same thing we would have
done if we’d failed to deduce T
originally — we simply discard the template from further
consideration and move on.
For more on SFINAE, see several of my conference talks:
- Template Normal Programming, Part 1 (CppCon 2016)
- Template Normal Programming, Part 2 (CppCon 2016)
- A Soupçon of SFINAE (CppCon 2017)
SIOF
The “Static Initialization Order Fiasco.”
C++ guarantees ([expr.const]/2) that certain kinds of global
initializations — like int i = 42;
— will get baked into the data section. The C++20 standard even
adds a new keyword, constinit
, so that you can write
constinit int i = i_sure_hope_this_function_is_constexpr(6, 9);
which means that the compiler must put it in the data section or else give you a compiler error.
But for dynamically initialized global variables —
std::string s = "hello";
std::string t = s + " world";
If s
and t
are defined in the same TU, then C++ guarantees their initializers will run
in the order you’d expect. But if they’re defined in two different TUs, the linker might decide to
order the initializer for t
before the initializer for s
. So t
’s initialization uses s
as a string before s
has actually been constructed, leading to UB at runtime.
(Wandbox.)
SMF
“Special member function.” According to the C++20 standard (special/1),
the special member functions are “default constructors, copy constructors, move constructors,
copy assignment operators, move assignment operators, and prospective destructors.”
(The phrase “prospective destructor” acknowledges that a C++20 class may have many constrained
member functions all named ~T
, all of which are special member functions, but only one of which
will ultimately be selected as the destructor for the class.)
Notice that “specialness” is imperfectly correlated with the ability to =default
the member function.
Foo::Foo(int=0)
is a default constructor (and thus an SMF of Foo
), but cannot be =default
ed.
In C++20, operator<=>
and operator==
can both be defaulted, yet neither of them is an SMF.
STL
The “Standard Template Library.” This name originally referred specifically to the library developed by Alexander Stepanov and others at Silicon Graphics (the “SGI STL”; PDF documentation here) and proposed for standardization in 1993. Ironically, the name “Standard Template Library” far preceded any ISO standard for C++!
The SGI STL became the basis for a lot of the C++98 Standard Library. Since “SL”
doesn’t make for a great acronym, and since most of the standard library is templated in some way,
it is common for C++ programmers to refer to the entire C++ standard library as “the STL.”
Others might use the term more narrowly to refer to the parts of the library that deal with
containers, iterators, algorithms, allocators, adaptors, and function objects (such as std::less
),
excluding for example std::shared_ptr
and std::valarray
.
Still others might include only those parts of the modern Standard Library inherited directly from SGI,
excluding for example std::unordered_set
and std::move_iterator
.
Notably, iostreams were invented by Bjarne Stroustrup circa 1984; therefore they are part of the “STL” only in that phrase’s most inclusive (and most common) sense.
In Effective STL (2008), Scott Meyers defines his use of the term “STL”
as “the parts of C++’s Standard Library that work with iterators.” (He explicitly excludes the
container adaptors stack
, queue
, and priority_queue
, even though those were part of the SGI STL,
on the grounds that they are not iterable.)
As for Mastering the C++17 STL (O’Dwyer, 2017), I freely admit that
I used “STL” purely as a short form of the phrase “Standard Library” optimized for book covers and
search engines.
The other meaning of “STL” in C++ contexts is as the initials and preferred nickname of Stephan T. Lavavej (pronounced “Steh-fin Lah-wah-wade”). As of 2019, STL happens to work on MSVC’s implementation of the STL.
TBAA
“Type-based alias analysis.” This is what lets the compiler conclude that in the following
snippet (Godbolt), *pi
and *pf
cannot alias each other.
int foo(int *pi, float *pf) {
*pi = 42;
*pf = 3.14;
return *pi;
}
Since it would be UB to write 3.14
into a memory location and then read an int
back from that same memory location, and UB never happens in a correct program, the compiler
infers that pi
must point somewhere different from pf
. The generated code “remembers” the
value that was stored into *pi
and returns 42 directly from the function, instead of generating
a load from memory.
On the other hand, if you changed pi
from int*
to float*
, you would see the load happening,
because loading a float
from a memory location that contains a float
is not UB.
So in that case the compiler couldn’t assume that pi
and pf
don’t alias. Other types that
might alias float
include plain char
and
struct ContainsFloat
.
Type-based alias analysis is also known as “strict aliasing,” because the analysis
can be disabled with the compiler option -fno-strict-aliasing
.
TCO
“Tail call optimization.” This is a compiler optimization that takes what appears in the source code
as a function call (which usually pushes an activation frame onto the call stack) and turns
it into a plain old jump (which does not push a frame on the call stack). This is possible only
when the function call appears at the very tail end of the function — something like return bar()
.
The compiler is saying, “Look, I know bar
is going to end by returning to its caller; that’s just
me, and I have nothing left to do. So let’s just trick bar
into returning to my caller!”
Tail call optimization is often possible for return bar();
but not for, e.g., return bar()+1;
— because there I do have something left to do: add 1 to the result. In some languages and/or
programming idioms, instead of doing that plus-1 myself, I can tell bar
to do it and
take my own stack frame out of the picture; this is known as continuation-passing style (CPS).
In the case that the function being tail-called is the same as the current function (recursively), you might call it tail recursion optimization (TRO).
See “It’s not always obvious when tail-call optimization is allowed” (2021-01-09).
TMP
“Template metaprogramming.” Personally, I find that the acronym “TMP” has a vague whiff of C++98 which is not shared by the simple spelled-out word “metaprogramming.”
If you have lots of structs with ::type
and ::value
members, you’re probably doing TMP.
TU
“Translation unit.” This is what language-lawyers say instead of “.cpp file.” When you invoke the compiler
to translate your C++ code into machine code, you give it just one translation unit (TU) at a time.
Formally, a “TU” is the sequence of tokens that you get
after preprocessing an input file so that all its #include
directives have been expanded and all its
#if
directives have been resolved.
If you #include
a file, then the text of that file becomes part of your translation unit.
In C++20 Modules, if you import
a module then that module’s “module interface unit” (MIU)
does not become part of your translation unit. Rather, a module interface unit is itself a
kind of translation unit: it is translated in a separate step.
UB
“Undefined behavior.” C++ shares this notion with C, and it means the same thing to both languages:
behavior for which this document imposes no requirements —N4810 [defns.undefined]
This document imposes no requirements on the behavior of programs that contain undefined behavior. —N4810 [intro.abstract]/4
Notably, in contrast to “IFNDR,” the standard acknowledges that a program containing UB is still a well-formed program (even if its runtime behavior is undefined).
You might also see the occasional “IDB” (“implementation-defined behavior”); and if someone wrote “USB” I might grok from context that it meant “unspecified behavior.”
UDL
“User-Defined Literal,” as in,
the C++11 feature where you declare an operator ""_myudl
and then are able to use 12345_myudl
and/or "hello world"_myudl
as “literals” in your code. The Standard Library defines several
overload sets’ worth of UDLs inside namespace std::literals
.
The paper standard says that all programmer-defined UDLs
must have names starting with _
(underscore). Of course, they cannot start with two underscores
or an underscore followed by a capital letter, because those are also verboten.
UFCS
“Universal Function Call Syntax,” a name for some kind of feature that would let you write x.f(y)
and f(x, y)
interchangeably. Nobody really knows how to get this into C++. For an excellent
rundown of all the different proposals and their differences and difficulties,
see Barry Revzin’s blog post “What is UFCS anyway?”
(April 2019).
VLA
“Variable-length array.” This is the C99 feature that lets you write
int main(int argc, char **argv) {
int arg_values[argc - 1];
}
VLAs are not part of standard C++ (and never will be).
Furthermore, C11 made VLAs a “conditional feature” which even C compilers needn’t support. C11-and-later
compilers which don’t support VLAs are supposed to define __STDC_NO_VLA__
to 1
.
VTT
“Virtual table table.” In the Itanium ABI, this data structure sits alongside the more familiar “vtable” and is used during construction and destruction of classes with virtual bases. See “What is the virtual table table?” (2019-09-30).