C++ Language Features Flashcards
These flashcards cover the C++ language features from C++11 - C++20. Footnotes will show which C++ version the feature appeared in.
What are coroutines?
C++20
Coroutines are special functions that can have their execution suspended and then resumed. C++20’s coroutines are stackless meaning that their state is allocated on the heap (unless optimized by the compiler).
Defining a coroutin means the co_return
, co_await
, or co_yield
keywords must be present in the function body. Under the hood,co_yield
uses co_await
C++20
What is a concept?
C++20
A concept is a named compile-tile predicate which contrains types. Constraints model semantic requirements (e.g. type is numeric or hashable). Because constraints are evaluated at compile-time, they can provider more meaningful error messages and runtime safety.
It takes the following form:
template < template-parameter-list > concept concept-name = constraint-expression;
For a real example, here’s a concept that limits T to signed integrals.
// Limit `T` to both the `integral` constraint and signedness. template <typename T> concept signed_integral = integral<T> && std::is_signed_v<T>;
There are lots of ways to use concepts:
* For constraining function parameters
* For constraining auto deduced variables
* For contraining template params
* And many other forms that use the auto
keyword and work with lambdas
* Using the requires
keyword to specify requirements
C++20
How would you use template syntax to create a lambda?
C++20
auto f = []<typename T>(std::vector<T> v) { // ... };
C++20
Give an example of a range-based for loop using a std::vector
initializer.
C++20
for (auto v = std::vector{1, 2, 3}; auto& e : v) { std::cout << e; } // prints "123"
C++20
Is is preferred to implicitly capture or explicitly capture this
? Give an example.
C++20
struct int_value { int n = 0; auto getter_fn() { // BAD: // return [=]() { return n; }; // GOOD: return [=, *this]() { return n; }; } };
C++20
What would you use to hint to the optimizer that a certain outcome has a high probability of being executed?
C++20
This question is getting at the [[likely]]
and [[unlikely]]
attributes.
For example:
int random = get_random_number_between_x_and_y(0, 3); if (random > 0) [[likely]] { // body of if statement // ... }
C++20
What does the explicit
keyword do?
C++20
You can describe the explicit keyword in C++ as a language feature that is used to prevent implicit conversions and copy-initialization.
Basic Definition: The explicit keyword in C++ is used before a constructor to prevent the compiler from using that constructor for implicit conversions. It means that the constructor is only callable if the programmer explicitly calls it.
Preventing Implicit Conversions: Without explicit, C++ allows constructors that can be called with a single argument to be used for implicit type conversions. This might lead to unexpected behavior if the compiler automatically converts types using these constructors. By marking a constructor as explicit, you ensure that such conversions are only made when the programmer explicitly requests it, improving code clarity and safety.
Copy Initialization: explicit also applies to preventing copy initialization. For instance, if you have explicit MyClass(int x);, then writing MyClass obj = 10; would be disallowed, whereas MyClass obj(10); or MyClass obj = MyClass(10); would be fine.
Good Practice: Using explicit is generally considered good practice for single-argument constructors (and in C++11 and later, for constructors that can be called with one argument), as it makes the programmer’s intentions clear and prevents accidental type conversions that can lead to subtle bugs.
C++20
Give an example of bringing an enum’s members into scope to improve readability in a switch statement.
C++20
Use the using enum <my_enum>
syntax:
enum class rgba_color_channel { red, green, blue, alpha }; std::string_view to_string(rgba_color_channel my_channel) { switch (my_channel) { using enum rgba_color_channel; case red: return "red"; case green: return "green"; case blue: return "blue"; case alpha: return "alpha"; } }
C++20
Explain all terms in the following section of code:
template <typename... Args> auto f(Args&&... args){ // BY VALUE: return [...args = std::forward<Args>(args)] { // ... }; }
C++20
This piece of code demonstrates capturing a parameter pack in a lambda expression.
The function f
is a template function that accepts a variadic number of arguments. typename... Args
defines a parameter pack named Args that can take any number and type of arguments. Args&&... args
uses forwarding references (also known as universal references), which means it can bind to both lvalues and rvalues.
Inside the function, std::forward<Args>(args)...
is used. This is an example of perfect forwarding. It forwards the arguments to another function or a lambda while preserving their value category (lvalue or rvalue). It’s essential in a variadic template to maintain the original type and value category of each argument.
The lambda is defined inside the function f
. Lambdas can capture variables from their enclosing scope, and in this case, the lambda is capturing variables from the parameter pack. Inside the lambda, you can use these captured arguments. Since they are captured by value (after being perfectly forwarded), the lambda has its own copies, and their original type and value category are preserved at the point of capture.
This is particularly useful in template metaprogramming and when writing generic code that needs to handle an arbitrary number of arguments in a type-safe manner.
C++20
How would you capture parameter packs by reference using a lambda?
C++20
Here’s an example of capturing parameter packs by reference using a lambda:
template <typename... Args> auto f(Args&&... args){ // BY REFERENCE: return [&...args = std::forward<Args>(args)] { // ... }; }
C++20
What does the constinit
specifier do?
C++20
The constinit
specifier requires that a variable must be initialized at compile-time.
const char* g() { return "dynamic initialization"; } constexpr const char* f(bool p) { return p ? "constant initializer" : g(); } // OK: constinit const char* c = f(true); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time. constinit const char* d = f(false);
C++20
Explain the following core language Concepts added in C++20 standard library:
same_as
derived_from
convertible_to
common_with
integral
default_constructible
C++20
-
same_as
- specifies two types are the same -
derived_from
- specifies that a type is derived from another type -
convertible_to
- specifiers that a type is implicitly convertible to another type -
common_with
- specifies that two types share a common type -
integral
- specifiers that a type is an integral type (e.g. int, char, unsigned long, uint32_t) -
default_constructible
- specifies that an object of a type can be default-constructed
C++20
What comparison Concepts have been added to the standard library for C++20?
C++20
boolean
- specifies that a type can be used in Boolean contextsequality_comparable
- specifies that operator==
is an equivalence relation
C++20
Explain the following object Concepts added to the standard library for C++20.
movable
copyable
semiregular
regular
C++20
-
movable
- specifies that an object of a type can be moved and swapped -
copyable
- specifies that an object of a type can be moved, swapped, and copied -
semiregular
- specifies that an object of a type can be moved, swapped, copied, and default constructed -
regular
- specifies that a type is regular meaning is is bothsemiregular
andequality_comparable
C++20
What callable Concepts were added to the standard library for C++20?
C++20
-
invocable
- specifies that a callable type can be invoked with a given set of argument types -
predicate
- specifies that a callable type is a Boolean predicate
C++20
What is buffering in the context of output streams? How does synchronization relate?
C++20 Synchronized Buffered OutputStream
Buffering refers to storing data in a temporary memory area (a buffer) before sending it to the final destination (e.g. files, network sockets). This approach can enhance performance, especially for I/O operations, by reducing the number of calls to the underlying system for each output operation. Data is accumulated in the buffer and written in larger chunks.
Synchronization in this context refers to making the output operations thread-safe. In a multithreaded environment, different threads may attempt to write to the same output stream simultaneously. Without synchronization to ensure only one thread performs the output operation at a time, these operations could interleave, resulting in corrupted or jumbled output.
C++20 Synchronized Buffered OutputStream
What is the following code doing?
std::osyncstream{std::cout} << "The value of x is:" << x << std::endl;
C++20 Synchronized Buffered OutputStream
This code buffers the output operations for the wrapped output stream and ensures synchronization so that there’s no interleaving of the output.
C++20 Synchronized Buffered OutputStream
Since std::span
doesn’t propogate const
, how would you construct a read-only span?
C++20
To construct a read-only span, you can use the const
qualifier when constructing the span
like so: std::span<const T>
C++20
What is the purpose of std::span
?
C++20
A span is a type of view (i.e. non-owning) of a container that provides bounds-checked access to a contiguous group of elements. Views to not own their elements, more like holding references to their data, so they are cheap to construct and copy. Instead of having to maintain a pointer/iterator and a length field representing the number of elements, a span wraps both of those into a single object. Spans can be dynamically-sized or fixed-sized. A fixed-sized span will fail to compile for containers that don’t match the extent (i.e. specified size) of the span.
C++20
Why does the following code produce an error?
void print_three_ints(std::span<const int, 3> ints) { for (const auto n : ints) { std::cout << n << std::endl; } } print_three_ints(std::vector{ 1, 2, 3 }); // ERROR
C++20
We’re passing a temporary object, std::vector{...}
, to the function which means there’s an implicit conversion from std::vector
to std::span<const int, 3>
. This doesn’t compile because std::span
` with a fixed size expects the source to match the size exactly.
To fix this error, we can take two approaches:
1. Change the function signature to void print_three_ints(std::span<const int> ints)
to remove the fixed size
2. Explicitly create a span with the appropriate size like:
std::vector<int> vec = { 1, 2, 3 }; std::span<const int, 3> span(vec.begin(), vec.end()); print_three_ints(span);
C++20
C++20 adds a new <bit>
header. What function would I use to count the number of 1 bits in an unsigned integer?
C++20
Using std::string
, how can you tell if a string starts or ends with a provided substring?
C++20
You can use the new starts_with
and ends_with
member functions. This works with strings and string views in C++20.
std::string str = "foobar"; str.starts_with("foo"); // true str.ends_with("baz"); // false
C++20