This is a second part of the story on how the C and C++ languages came to be as we know them today. Here, we'll cover the official certification of C and C++, as well as the tools released from 1991 to the present day.
In 1994, the C++ language creator Bjarne Stroustrup published a book on how the C++ language was created—"The Design and Evolution of C++". In this book, the author pays special attention to general design principles and the course of the language evolution from C With Classes to its current state, describing what influenced certain architectural decisions. The book also covers many language concepts essential to modern C++.
Figure N1—The first edition cover of "The Design and Evolution of C++" book
Development of the Qt library began in 1991 with the ambitious goal to create "the world's best C++ GUI implementation library". Authors wanted this project to meet the growing need for efficient and flexible GUI development tools that would integrate into C++-based platforms and enable developers to focus on application functionality.
In the early stages of the library development, authors focused on ensuring a user-friendly GUI and designing an architecture that could be easily adapted to different operating systems with minimal code changes. In 1992, the library developers introduced a paradigm based on the concepts of "signals" and "slots". This idea radically changed the approach to event handling in programming and enabled devs to streamline connection between objects in a graphical interface. Later, in 1993, this concept was implemented in code.
In the same 1993, developers implemented the first graphical core of the library and started to create visual components, which became the basis for building user interfaces.
On May 20, 1995, the Qt 0.90 library was first publicly released.
In March 2009, the first version of Qt Creator came out. It is an integrated development environment designed to streamline the work with the Qt library.
Today, Qt is one of the most comprehensive frameworks in C++. Devs actively use it in a variety of areas, including desktop and mobile application development, as well as in embedded software development.
The first C++ standard, known as C++98, was released in 1998 to standardize all the features that had been added to the language since its introduction. The Annotated C++ Reference Manual highlighted the functionality that has been implemented with the release of this standard.
The use of templates has been standardized, allowing the creation of generic classes and functions that work with any data type. The use of exceptions has also been standardized.
To avoid name conflicts, namespaces
were added, uniting entities such as classes, objects, and functions in a separate scope. The bool
data type, as well as the true
and false
keywords were added to represent logical values.
The behavior of multiple inheritance and virtual functions has been standardized to ensure uniformity between compilers. The new
and delete
operators have also been standardized.
The standard library, particularly the standard template library, I/O thread library, complex numbers library, containers (vector, doubly linked list, set, map), adapters (stack, queue, priority queue), iterators, algorithms, function objects have also been standardized.
After the C++98 introduction, many people were unhappy with the lack of some functionality in the STL. So, a new project has appeared—Boost—a collection of class libraries that utilize C++ features and provide a high-level cross-platform interface for solving various tasks.
This project is a kind of testing ground for various language extensions and libraries that may be included in the next standard.
In 1998, the 1.0 version of the Unreal Engine game engine was released. At that time, it was one of the first universal engines that combined graphics and physics engines, artificial intelligence, file and network system management, and a ready-made environment for game development.
Unreal Engine 1.0 featured some truly revolutionary technologies for the time, such as the use of a dynamic scene graph that allowed to add various effects to overlay on surfaces.
The first version of the engine was released for Windows and Macintosh platforms, but a little later it was successfully adapted for GameCube, PlayStation 2, and Xbox. Today, Unreal Engine supports most platforms: Windows, PlayStation 4 and 5, Xbox Series, Xbox One, Nintendo Switch, macOS, iOS, Android, SteamVR, Oculus, Linux, SteamDeck, and more.
A year later, an improved version of the engine, also known as Unreal Engine 1.5, was released with significant functionality enhancements. The version included: facial animation support for characters, an increased to 1024×1024 maximum texture resolution, and an extensible particle system.
After the first standard for the C language was adopted, it remained relatively unchanged for some time. But in 1999, the new ISO/IEC 9899:1999 standard was published.
It added some features that were previously implemented as extensions for some compilers.
The inline
functions were added. When a programmer calls them, a compiler replaces the function call with the function body.
New data types appeared: long long int
, additional extended integer types, the _Bool
explicit logical data type, and the _Complex
type to represent complex numbers.
Variable-length arrays whose length is calculated at runtime were added. Single-line comments (//
) were supported both in BCPL and C++. Also, variadic macros (variable arity macros) and compound constants were added.
This standard was adopted as an ANSI standard in May 2000.
This build system was originally developed for the Insight Toolkit project sponsored by the U.S. National Library of Medicine (NLM). The project required support for multiple platforms. NLM has teamed Kitware with other companies and universities to develop this toolkit. They called the system CMake. It made the process of building and managing projects much easier
CMake automatically generates build files for different operating systems and compilers, which has become especially important for large projects that require versatility and flexibility. CMake soon gained widespread popularity in the software development world due to its capability to work across platforms, support complex dependencies and integrations, and provide a high degree of automation in the building process.
In 2003, the C++03 standard came out. However, it didn't include many features. C++03 is a follow-up to the C++98 standard, fixing its errors and defects. During its development, many reported issues were reviewed and fixed in the standard. The only thing that has been added directly to the language since C++03 is value initialization, which executes when an object is created with an empty initializer.
However, in 2005, the Draft Technical Report on C++ Library Extensions was published, describing proposals for additions to the C++ library standard.
For example, the paper proposed:
reference_wrapper
class that creates a wrapper for a reference;function
, bind
, result_of
, and mem_fn
function objects;tuple
type for tuples;array
type for fixed-length arrays;regex
header file for working with regular expressions.This document was published as the ISO/IEC TR 19768:2007 standard in 2007. The features it proposed found their way into the language a bit later.
In 1987, the GCC compiler suite was developed under the GNU project. Originally, the GNU C Compiler supported only a compiler for C, but later support for other languages such as C++ was added. Its full name changed to the GNU Compiler Collection.
In 2007, Clang—a compiler for C-like languages based on the LLVM framework—emerged as an alternative to GCC.
One of the project goals was to implement incremental translation, enabling tighter integration between the compiler and the IDE, an area where GCC had issues. Clang modular design can be effectively used in IDEs for code indexing, syntax highlighting, and things like this.
Another major difference between Clang and GCC is their focus: the GNU Compiler Collection (GCC) prioritizes code generation, while Clang serves as a comprehensive framework for parsing, indexing, static analysis, and compilation. Additionally, during parsing, Clang preserves the original source code structure, accurately converting it into an abstract syntax tree (AST) without simplifications.
Nowadays, the project involves developers from Google and Apple, among others.
The same year: the release of Viva64, which became PVS-Studio 3 years later
On December 31, 2006 (just a few hours before 2007) the Viva64 analyzer was launched. The analyzer was designed to detect errors when migrating to 64-bit systems.
The second version of the analyzer was released in 2008. The third one, dated 2009, united Viva64 and VivaMP (a tool for analyzing issues in multithreaded programs built on OpenMP) into a single tool—PVS-Studio.
Today, PVS-Studio is a static analyzer and SAST solution for C, C++, C#, and Java. You can try the tool at this link.
A new version of the C++ language standard was released in 2011. It introduced changes to the language core, an extended standard library, which included all of the TR1 proposals except for special math functions.
Multithreading support was added to the core, along with improvements to generic programming, unified initialization, and overall performance enhancements. For example, move semantics appeared—a set of semantic rules and C++ tools designed to move objects whose lifetime is about to expire instead of copying them, which helps avoid costly duplication. To enable it, rvalue and forwarding references were introduced, along with move constructors and the move assignment operator. Additionally, the standard template library was updated with functions to support move semantics, such as std::move
and std::forward
.
The auto
placeholder type specifiers appeared in this standard, indicating that the type of the declared variable will be automatically derived from its initializer. The decltype
specifier also appeared. It outputs the type of the declared entity or the type and category of the passed expression value.
The update introduced the capability to declare functions as deleted using = delete;
and instruct the compiler to automatically generate special function bodies using = default;
. Lambda expressions appeared in the language.
New specifiers—final
and override
—were introduced for class and virtual function declarations. Both force the compiler to check whether any virtual function from the base class is overridden. This gets rid of a frequent error in C++, when instead of overriding a virtual function from the base class, a programmer actually hides it by adding a new function. Also, the first specifier can be additionally applied to the class definition, to prevent inheritance from such a class.
The std::nullptr_t
type and the nullptr
literal of this type were added. With the literal, a developer can set the pointer state to null instead of using the NULL
macro. It also helps avoid ambiguity in function calls when there are two overloads with an integral type and a pointer. A similar literal later appeared in C, but only with the C23 standard.
The constexpr
specifier also appeared. It allows to move the evaluation of the function value to the compilation stage, and further to use it where only constant compile-time expressions are allowed. I recommend reading this article that covers the design and evolution of constexpr
in C++.
A range-based for
loop was added. It enables a developer to iterate over an object (such as a standard container or a user-defined type), provided that the begin
and end
member functions are defined for the type. These functions should return iterators to the first element and to the element following the last element. This feature was added from the Boost.Foreach library. The static_assert
(Boost.StaticAssert) keyword was also added from the library, allowing assertions to be checked at compile time.
There have also been changes to the standard library. For example, here's what was added:
std::unordered_set
, std::unordered_map
and their multi-versions;std:array
and std::forward_list
;std::unique_ptr
, std::shared_ptr
, std::weak_ptr
;std:: linear_congruential_engine
, std::mersenne_twister_engine
, std:: subtract_with_carry_engine
), adapters (std: :discard_block_engine
, std:: independent_bits_engine
, std:: shuffle_order_engine
), and distribution functions (uniform, normal, Poisson, Bernoulli);std::thread
, std::mutex
, std::lock_guard
, std::unique_lock
, std::condition_variable
, std::future
, std::promise
, ....Note. We recently posted an article on why std:array
in C++ is not slower than an array in C. You can read it here.
On August 18, 2014, the C++14 standard was released, intended as an extension to the previous standard, C++11. Although this standard is considered a minor one, the list of changes is not as small as it may seem.
There are several reasons for this. Firstly, this standard polished C++11 and fixed various defects (a total of 276 core and 158 library defects). Secondly, variable templates were added to enable the definition of a variable family or static data members. Additionally, features that are essential for writing C++ code today were introduced, for example:
auto
output for return function types;decltype(auto)
;constexpr
functions, allowing almost any code to be evaluated at compile time;Here comes the C++14 killer feature: it became possible to eliminate or merge some dynamic allocations. In the first case, the compiler can now provide a buffer without calling a replaceable allocation function, for example, by allocating it on the stack. In the second case, when the new e1
expression is evaluated, the compiler can allocate memory for another new e2
expression as well. This optimization is possible if the following conditions are met:
e1
strictly contains the lifetime of the object allocated by e2
.e1
and e2
must call the same replaceable global allocation function;e1
and e2
will be caught by the same handler.A new standard came out in December 2017. Let's go through the features of the updated standard.
Fold expressions appeared. A fold expression is a C++ syntax element designed to fold packs of variadic template parameters using an optional initial value. It helps avoid cumbersome recursive calls and allows to apply operations to all individual pack arguments in a concise manner. Also, auto
for non-type template parameters was added.
The if
and switch
constructs with initializers were added. Another significant addition was if constexpr
, which enables compile-time branching, reducing the need for SFINAE-heavy code. The constexpr
functions were also improved to support lambda expressions and declare them as constexpr
. The *this
lambda capture was introduced, which copies the state of the current object.
A mechanism of structured binding appeared, enabling convenient decomposition of pairs, tuples, and other similar objects.
Changes to the standard also affected the standard library. New types, tools for memory management, and compile-time programming were added to it.
Note. In 2017, when the standard was released, we published an article with a detailed breakdown of new C++17 features. You can read it here.
In December 2020, after another three-year cycle, the C++20 standard was released.
C++ 20 introduced constraints that enable developers to define requirements for template arguments. The named sets of constraints were called concepts. Constraint violations are found at the compilation stage, which makes error detection quite easy.
The language introduced modules that allowed declaration and definition exchanges between translation units. Coroutines—functions that can stop execution to resume it later—were also added.
The spaceship-operator (<=>
) appeared, which allowed to transfer data member comparison to the compiler. The implementation of this feature resulted in the non-type template parameter for custom types.
Some features were allowed for constexpr
, such as calling virtual functions or using dynamic_cast
. The consteval
and constinit
specifiers also appeared. The first specifies that each function call should create a compile-time constant. The second indicates that the variable is initialized with a static storage duration or thread storage duration compile-time constant. It can be modified later, unlike constexpr
.
The constexpr
allocators appeared, allowing even containers requiring memory allocation to be used within the constexpr
range. However, dynamic allocation cannot leave the constexpr
function.
Functionality check macros were added. Their function is to provide a simple and portable way to test the language features introduced since the C++11 standard.
Starting with C++20, range-based for
loops got support for pre-initialization, and lambda expressions were enhanced with batch extensions to lambda capture.
C++23 came out last October. The process of developing this standard differed from previous ones because almost all committee meetings were held remotely.
This standard introduced explicit object member functions. All non-static functions in classes always take an additional parameter, which is the object itself (the this
pointer). Before C++23, the function used to implicitly take it, but in the new standard enables to explicitly specify this parameter when declaring a function.
To optimize compilation time, if consteval
was added to check weather a function is executed in a constant context.
Lambda expressions got attribute support, and multidimensional arrays appeared for multidimensional arrays.
It became possible to mark unreachable code using std:unreachable
, helping compiler optimization.
String formatting has been improved in the standard library, and new std::flat_map
and std::flat_set
adapters have been added.
You may have noticed that no standard for C after C99 was described in this article. Why? All language standards that followed were focused on refining the language features that have been already introduced, as well as on additions to the standard library.
The most notable language features came out with the C11 standard, as it has undergone some changes. For example, multithreading support and type-generic expressions using the _Generic
keyword.
Along with C++23, C23 was also released in 2024. This standard removed the function definition format from K&R. It had a major drawback—the compiler couldn't check argument type compatibility when calling functions. This change resulted in more similarities between C and C++.
In addition, the language got some features that were previously added to C++:
static_assert
as a keyword instead of _Static_assert
introduced in C11;thread_local
as a keyword instead of the macro expanded as _Thread_local
;bool
type with pre-specified true
and false
values;auto
;realloc
behavior is now undefined when the allocation size is 0;nullptr
and nullptr_t
.The #embed
directive also appeared, enabling the easy inclusion of binary data in an executable program image without an external script.
Here, our time travel comes to an end, and we are back in the present. C and C++ are truly iconic languages that, despite their age, remain at the top of the most popular programming languages list to this day. I hope you too found this journey through history fascinating.
0