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.


“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).)


“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.


“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).


“Argument-dependent lookup.” See “What is ADL?” (2019-04-26).


“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.”


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!


“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.


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.”


“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 and 4048f5c3? See “Bit patterns of float (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.


“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.


The “Curiously Recurring Template Pattern.” See Wikipedia. Occasionally misquoted as “Curiously Recursive.”


“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.)


“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"));


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).


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++.”


“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.”


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).


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).


“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.


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).


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.


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.


“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 exported; you can modify things in module hello’s PMF without needing to recompile other TUs that import hello.

#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).


“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).


“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


“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.


“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.


“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


“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).


“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.


Microsoft Visual C++ — in C++ contexts, essentially a synonym for Microsoft Visual Studio (VS or MSVS).


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));


“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.


“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_ts ending with wchar_t(0)).


“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.


[[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.


“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;
    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).


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.


“Object-oriented programming,” of course. Sometimes shortened even further to “OO,” as in “OO principles.”


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.


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.


“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.


“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).


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 false_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 true_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).


Plain Old Data.” This term has been deprecated in C++20, along with the type trait std::is_pod<T>.


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!”


“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.”


“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.


“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).


“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() {

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).


“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


template<class T>
class vector_iterator { ... };

template<class T, class A>
class vector {
    using iterator = vector_iterator<T>;

// Fewer iterator classes: hotter code
    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.


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.


“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:


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.)


“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 =defaulted. In C++20, operator<=> and operator== can both be defaulted, yet neither of them is an SMF.


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.


“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.


“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).


“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.


“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.


“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.”


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.


“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).


“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.


“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).

Posted 2019-08-02