C++ Interview Questions: Practice Real Technical Interview Problems
Reviewed by Mark Dickie · Last updated
14 questions on this page you can answer and see marked on the spot. Go to the first one
C++ is a compiled, statically typed programming language that provides direct control over memory, hardware resources, and low-level system behavior. For interviews, expect questions on pointer and reference semantics, RAII, the rule of three/five/zero, template fundamentals, and STL containers and algorithms. Memory ownership and object lifetimes are the most frequently tested areas, especially smart pointers introduced in C++11. Modern C++ through C++23 added move semantics, lambdas, constexpr evaluation, and concepts, all of which appear in current interview loops.
What does a C++ interview typically test?
| Topic area | What gets asked | Common difficulty |
|---|---|---|
| Memory management | Raw pointers, smart pointers, ownership models, memory leaks | 3–4 |
| Object-oriented design | Virtual functions, vtables, multiple inheritance, object slicing | 3–4 |
| Templates | Function templates, class templates, SFINAE, concepts | 4–5 |
| STL | Container choice, iterator invalidation, algorithm complexity | 2–4 |
| Modern C++ features | Move semantics, lambdas, constexpr, structured bindings | 3–4 |
| Concurrency | std::thread, mutexes, atomics, data races | 4–5 |
How should you prepare for a C++ interview?
- Write small programs from scratch that exercise raw pointers, references, and smart pointers side by side. Knowing when each is appropriate is a recurring interview question.
- Study the rule of three/five/zero until you can explain when the compiler generates each special member function and why.
- Practice choosing the right STL container for a given problem (vector vs list vs unordered_map) and justify the time-complexity trade-offs out loud.
- Read through the C++11 through C++20 feature set and be ready to explain move semantics, perfect forwarding, and how
autoanddecltypededuce types. - Work through at least one concurrency problem using
std::threadandstd::mutexso you can describe data races and how atomics prevent them.
Key facts
- Tarmac has 96 C++ interview questions on this topic, 25 of them on this page, at difficulty 1–5 of 5.
- Tarmac tracked 832 job postings asking for C++ in September 2026.
- Roles asking for C++ advertise a median base salary of £70,000, across 231 job postings as of September 2026.
- Tarmac last reviewed these C++ interview questions on 13 October 2026.
At a glance
| Questions | 25 shown · 96 in the bank |
|---|---|
| Difficulty | 1–5 of 5 |
| Formats | Multiple answer, Flashcard, Code output, Multiple choice, True / false, Ordering, Short answer, Find the bug, Fill in the blank |
What you'll review
- raii
- rule of three five
- smart pointers
- const correctness
- move vs copy
- virtual functions vtables
- dangling references
- object slicing
Practice questions
Try one before you open the answer. Pick an option and press Check; it's marked on the spot.
Which of the following statements about RAII (Resource Acquisition Is Initialization) in C++ are correct?#
Options
Pick every one that applies.
Show answer
RAII in C++ ties resource acquisition to object construction and resource release to object destruction (via the destructor), providing deterministic cleanup without relying on a garbage collector. The correct statements are that acquisition happens during construction, release happens during destruction, and cleanup is deterministic.
RAII ties resource acquisition to object construction (resource acquisition being tied to object construction) and resource release to object destruction (resource release being tied to object destruction), giving deterministic cleanup (deterministic cleanup). It does not rely on a garbage collector (the claim that RAII depends on a garbage collector is wrong) — destructors run at well-defined times such as when a stack object goes out of scope or a heap object is deleted.
What is the Rule of Three in C++?#
Show answer
If a class manages a resource (e.g., a raw pointer), you must explicitly define a destructor, a copy constructor, and a copy assignment operator. The compiler-generated defaults perform shallow copies, which cause double-free or leak bugs, so all three must be written together.
This is a foundational C++ recall question. The Rule of Three states that any class that needs a user-defined destructor, copy constructor, or copy assignment operator needs all three, because the compiler-generated versions only do member-wise (shallow) copies that are unsafe for owned resources.
Which of the following are true about a stack-allocated object of a class type in C++?#
Options
Pick every one that applies.
Show answer
A stack-allocated C++ object has its destructor called automatically when it goes out of scope, can manage resources via its destructor (RAII), and its constructor runs at the point of declaration before later statements in the block. It does not require an explicit delete — that is only for heap-allocated objects.
A stack-allocated object's destructor is invoked automatically at end of scope, so no explicit delete is needed — "It requires an explicit delete expression to destroy it when it goes out of scope" is wrong — delete is for heap objects. Its destructor can release a resource such as a file handle or lock, which is the essence of RAII. The constructor executes at the point of declaration, before the subsequent statements in the block.
What is the exact output of the following C++ program?#
#include <iostream>
#include <memory>
struct A {
A() { std::cout << "A() "; }
~A() { std::cout << "~A() "; }
};
int main() {
std::unique_ptr<A> p1 = std::make_unique<A>();
{
std::unique_ptr<A> p2 = std::make_unique<A>();
}
std::cout << "end ";
return 0;
}Show answer
A() A() ~A() end ~A()
p1 is constructed first, printing A() . Inside the inner block, p2 is constructed, printing A() . When the block ends, p2 (a unique_ptr) is destroyed, which deletes its object and prints ~A() . Then end is printed. Finally, when main returns, p1 is destroyed, printing ~A() .
What problem does RAII (Resource Acquisition Is Initialization) solve?#
Options
Show answer
RAII (Resource Acquisition Is Initialization) ties a resource's lifetime to an object's lifetime: the constructor acquires the resource and the destructor releases it. Because C++ guarantees a local object's destructor runs when it goes out of scope — including during stack unwinding from a thrown exception — RAII gives automatic, exception-safe cleanup with no explicit cleanup block needed. std::unique_ptr, std::lock_guard, and std::fstream are standard-library RAII wrappers built on this pattern.
RAII binds a resource — heap memory, a file handle, a mutex lock, a socket — to the lifetime of a stack-allocated object: the constructor acquires it, the destructor releases it. Because C++ guarantees local objects' destructors run during stack unwinding (including when an exception propagates through the scope), RAII gives automatic, exception-safe cleanup with no explicit finally-style block. std::unique_ptr, std::lock_guard, and std::fstream are all RAII wrappers around a raw resource. The other options describe unrelated or incorrect properties: RAII says nothing about constness, doesn't guarantee zero overhead, and specifically exists so cleanup happens at scope exit, not merely at program exit.
You're storing a pointer to a heap object that has exactly one owner for its entire lifetime, and you want zero reference-counting overhead. Which smart pointer fits?#
Options
Show answer
std::unique_ptr is the right fit for a single, exclusive owner: it's movable but not copyable, and because exactly one unique_ptr ever owns the object, it needs no reference count — just a raw pointer and a deleter, so it's effectively as cheap as a raw pointer while still freeing automatically. std::shared_ptr pays for an atomic reference count to support multiple simultaneous owners, which this scenario doesn't need. std::weak_ptr doesn't own the object at all — it observes a shared_ptr without keeping it alive.
std::unique_ptr models exclusive ownership: it can be moved (transferring ownership) but not copied, and because only one unique_ptr ever owns the object, it needs no reference count — just a raw pointer and a deleter, making it effectively as cheap as a raw pointer while still freeing automatically. std::shared_ptr is for the different case of multiple simultaneous owners, and pays for an atomically-updated control block to track them. std::weak_ptr doesn't own anything — it exists to observe a shared_ptr-managed object without extending its lifetime, typically to break reference cycles. A raw pointer with manual delete reintroduces exactly the leak/double-free risk smart pointers exist to remove, so it's never the right answer to a memory-safety question even though it technically avoids smart-pointer bookkeeping.
A const member function can still modify a data member of the class if that member is declared mutable.#
Options
Show answer
True. A const member function makes this a pointer-to-const, so it normally can't assign to any data member. mutable is the deliberate exception: it marks a specific member — typically an internal cache, a lazily-computed value, or a synchronization primitive — as modifiable even through a const object, so a class can expose a logically-const public interface while still doing internal bookkeeping.
True. const on a member function is a promise not to modify the object's logical state through that function — the compiler enforces this by making this a pointer-to-const inside the function, so any member normally can't be assigned to. mutable is the deliberate escape hatch: it marks a member (typically bookkeeping like a cache, a lazily-computed value, or a mutex used only for internal synchronization) as modifiable even through a const object or const member function. It exists precisely so a class can present a const, logically-immutable public interface while still doing internal caching under the hood.
What does it mean for a C++ class to be an "RAII type"?#
Show answer
Its constructor acquires a resource (memory, a file handle, a lock, a socket) and its destructor releases it, so the resource's lifetime is bound to the object's scope. Because C++ guarantees destructors run on scope exit — including during stack unwinding from an exception — an RAII type gives automatic, exception-safe cleanup with no manual finally block. std::unique_ptr, std::lock_guard, and std::fstream are all RAII wrappers.
RAII is the core C++ idiom for deterministic resource cleanup, and it's the reason C++ code that uses smart pointers and standard containers rarely needs manual cleanup code at all.
Arrange the steps of a traditional (non-copy-and-swap) copy assignment operator for a class managing a raw dynamically-allocated array (int* data; size_t size;). Put them in the order they must execute for correct, leak-free behavior.#
Put these in order
Show answer
The correct order in a traditional copy assignment operator is: (1) check for self-assignment, (2) release existing resources, (3) copy the source's resources, (4) return *this. Self-assignment must be checked before freeing resources, and new resources must be acquired after the old ones are released to avoid leaks.
Self-assignment must be checked first; otherwise the next step would delete[] the very memory you still need. Releasing old resources second prevents leaks. Copying the source's resources third gives the object its new valid state. Returning *this last follows the convention that allows chaining like a = b = c.
These three design guidelines for C++ resource-managing classes evolved over successive standard revisions. Arrange them in the chronological order in which they became the prevailing guidance.#
Put these in order
Show answer
The Rule of Three (destructor, copy constructor, copy assignment) came first in C++03. The Rule of Five added move constructor and move assignment with C++11 move semantics. The Rule of Zero is the modern refinement: by using RAII member types, you need none of the five, letting the compiler generate correct defaults.
The Rule of Three dates to C++98/03: a class managing resources needs a destructor, copy constructor, and copy assignment operator. C++11 introduced move semantics, expanding the guidance to the Rule of Five by adding the move constructor and move assignment operator. The Rule of Zero is the modern best-practice refinement that emerged with widespread RAII types (smart pointers, std::vector, etc.): by composing members that manage their own resources, the compiler-generated defaults are correct and no manual special member functions are needed.
A class manually manages a raw int* acquired with new int[n] in its constructor and freed with delete[] in its destructor. Why does this class need the rule of five (or three), and what breaks if you skip it?#
Options
Show answer
The compiler-generated copy constructor and copy assignment operator would shallow-copy the pointer, so two objects end up owning (and eventually double-freeing) the same array
When you don't declare copy/move operations yourself, the compiler generates member-wise ones: for a raw pointer, that's a shallow copy of the address, not a deep copy of the pointed-to array. Copy (or default-generated move) then leaves two objects holding the same pointer; when both destructors run, the array is freed twice — undefined behavior, often a crash. The rule of three says if you need a custom destructor, copy constructor, or copy assignment, you likely need all three; the rule of five (C++11+) adds move constructor and move assignment for the same reason, since the implicitly-generated move would also just shallow-copy the pointer and leave the moved-from object still pointing at freed memory once destroyed. In practice, the preferred fix in modern C++ is the 'rule of zero' — hold the array in a std::vector<int> or std::unique_ptr<int[]> member instead, so the compiler-generated special members are already correct and no manual rule-of-five boilerplate is needed at all.
Which of these are true about std::move and move construction in C++? Select all that apply.#
Options
Pick every one that applies.
Show answer
std::move(x)doesn't move anything by itself — it's a cast that produces an rvalue reference tox, making it eligible to bind to a move constructor/assignment overload- After
T b = std::move(a);runs a move constructor,ais left in a valid but unspecified state — safe to destroy or reassign, but its value shouldn't be assumed - A move constructor typically transfers ownership of a resource (e.g. a heap buffer) by copying the pointer and nulling it out in the source, avoiding a deep copy
std::move is purely a cast (static_cast<T&&>) — it has no runtime effect of its own; it just makes the expression an rvalue so overload resolution can select a move constructor or move-assignment operator if one exists. A well-behaved move constructor steals the source's internal resource (pointer, buffer, handle) rather than deep-copying it, typically by copying the pointer and nulling the source's, which is what makes moves cheap for resource-owning types like std::vector or std::string. That's why the moved-from object is left 'valid but unspecified' — it's safe to destroy, reassign, or call methods with no preconditions on it, but its value is not to be relied upon. The claim that moving is guaranteed to be faster than copying for every type and the claim that std::move always triggers a move are wrong: nothing in the standard requires move to be O(1) for every type (a type with no move constructor falls back to its copy constructor, which may be O(n)), and passing an rvalue to a type with no move constructor simply resolves to the copy constructor instead of failing to compile.
Explain the rule of five: which five special member functions it covers, why defining one usually means you need the others, and what the 'rule of zero' alternative is.#
Show answer
The rule of five covers the destructor, copy constructor, copy assignment operator, move constructor, and move assignment operator. If a class manually manages a resource (like a raw pointer from new), it typically needs a custom destructor to free it — but the compiler-generated copy constructor and copy assignment would just shallow-copy that pointer, letting two objects free the same resource (double-free/UB), and the compiler-generated move would do the same shallow copy without properly nulling out the source, leaving a dangling pointer once the moved-from object is destroyed. So writing one of the five is usually a sign you need to think through all five explicitly (even if some are =deleted). The rule of zero is the preferred modern-C++ alternative: don't manage the raw resource yourself at all — hold it in a member that's already an RAII type (std::vector, std::unique_ptr, std::string), so the compiler-generated special members are already correct by composition, and you don't write any of the five.
This question separates candidates who've memorized 'rule of five' as a term from those who understand the mechanism: it's about the compiler-generated special members doing the wrong thing (shallow pointer copies) for a class that owns a raw resource, and a manually-managed resource needing all five to be considered together — destructor, copy ctor/assign, move ctor/assign — even if the right answer for some of them is to delete them. The strongest answers also mention the rule of zero, since in real production C++ the goal is almost always to avoid needing the rule of five at all by composing RAII types instead of manually managing raw resources.
A teammate says 'move semantics just make copies faster.' Explain more precisely what a move actually does differently from a copy, and give a concrete example of where it matters.#
Show answer
A copy duplicates a resource-owning object's underlying data — for a std::vector<int> with a million elements, that means allocating a new buffer and copying every element. A move instead transfers ownership of the existing buffer: the moved-to object takes the source's pointer, size, and capacity, and the source is left in a valid-but-empty state (typically null pointer, zero size) with nothing to copy at all. It isn't 'a faster copy' — for types like std::vector or std::string it's O(1) pointer-swapping instead of O(n) element duplication. A concrete case: returning a large std::vector by value from a function, or passing a temporary std::string into a constructor that stores it — before C++11 both required a full deep copy; move semantics (and the fact that a temporary is an rvalue, so it binds to the move overload automatically) make both essentially free.
The precise, interview-worthy answer is about mechanism, not speed as an unexplained fact: a copy duplicates the underlying data; a move transfers ownership of it (steals the pointer/buffer, nulls out the source) and is typically O(1) instead of O(n) for heap-owning types. Grounding it in a concrete case — returning a large container by value, or constructing from a temporary — shows the candidate understands where this actually shows up in real code, not just the vocabulary.
How does a virtual function call actually get dispatched to the right override at runtime?#
Show answer
Each class with virtual functions gets one vtable — a static array of function pointers, one per virtual function, built once at compile time. Every object of such a class carries a hidden vptr, set by the constructor, pointing at its own class's vtable (not its base class's, once a derived constructor has run). A virtual call through a base pointer/reference reads the vptr, looks up the function pointer in that vtable, and jumps to it — so the call resolves to whichever class actually constructed the object, not the static type of the pointer.
The vptr/vtable mechanism is what makes runtime polymorphism possible in C++ with no built-in reflection: dispatch is a pointer indirection through the object's own vptr, which is also why object slicing (copying only the base subobject) loses the derived vtable and breaks polymorphic behavior.
A Derived class inherits from Base and has a member object Logger log;. Order what happens when a Derived object is constructed.#
Put these in order
Show answer
Constructing a Derived object runs in a fixed order: base classes first (in their listed order), then non-static data members in declaration order (not initializer-list order), and only then does the derived class's own constructor body execute. Destruction runs in the exact reverse. This ordering is why calling a virtual function from a base class constructor never dispatches to a derived override — the object's vptr still points at the base class's vtable at that point, since the derived part hasn't been constructed yet.
C++ guarantees a fixed construction order for a derived object: base classes first (in the order they're listed in the class definition, not the initializer list), then non-static data members (in declaration order within the class, again regardless of initializer-list order), and only then does the derived class's own constructor body run. This matters in practice for a well-known gotcha: calling a virtual function from a base class constructor does NOT dispatch to a derived override, because at that point in construction the object's vptr still points at the base class's vtable — the derived part of the object doesn't exist yet. Destruction runs in the exact reverse order: derived destructor body, then members (reverse declaration order), then base classes (reverse order), which is also why a base class needs a virtual destructor to correctly destroy a derived object through a base pointer.
The following C++ code creates a doubly-linked structure using smart pointers. It compiles without errors but leaks memory when main returns. Find the line that should be changed to fix the leak.#
#include <memory>
class Node {
public:
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev;
Node() { }
~Node() { }
};
int main() {
auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();
a->next = b;
b->prev = a;
return 0;
}
Show answer
The bug is on line 6.
After main returns, a and b are destroyed. a's refcount drops to 1 (held by b->prev) and b's refcount drops to 1 (held by a->next), so neither block is actually freed — a classic circular reference. The fix is to change line 6 so that prev is declared as std::weak_ptr<Node>, breaking the ownership cycle. weak_ptr does not contribute to the reference count, so when main ends both refcounts reach 0 and the destructors run.
Two objects that each hold a std::shared_ptr to the other (a reference cycle) will leak memory, because their reference counts never reach zero.#
Options
Show answer
True. std::shared_ptr uses reference counting, and a cycle — two objects each holding a shared_ptr to the other — means each object's count always includes at least one reference from its partner, so neither count reaches zero and neither destructor ever runs, even after every external reference is gone. The standard fix is to make one direction of the relationship a std::weak_ptr, which observes the object without contributing to its reference count.
True. shared_ptr uses reference counting, and a cycle means each object's count includes a reference from the other object in the cycle. Even after every external shared_ptr to the cycle goes away, each object inside the cycle still holds a count of at least one (from its partner), so neither reference count ever drops to zero and neither destructor ever runs — a real leak, not just a delayed free. The standard fix is to make one direction of the relationship a std::weak_ptr instead (e.g., a child holding a weak_ptr back to its parent): weak_ptr observes the object without contributing to its reference count, so it doesn't keep the cycle alive, and it can be .lock()ed into a temporary shared_ptr only while the object is still known to exist.
This function compiles cleanly but callers see garbage or crashes. Which line is the bug?#
1| const std::string& firstWord(const std::string& sentence) {
2| std::string word = sentence.substr(0, sentence.find(' '));
3| return word;
4| }
5|
6| void printFirstWord(const std::string& s) {
7| const std::string& w = firstWord(s);
8| std::cout << w << std::endl;
9| }Options
Show answer
Line 3 — word is a local variable; returning a reference to it produces a dangling reference the moment firstWord returns
word is a local variable with automatic (stack) storage duration — it's destroyed when firstWord returns. Returning word by reference (line 3) hands the caller a reference to storage that no longer holds a valid std::string; that's a dangling reference, and using it (line 8's std::cout << w) is undefined behavior — it might print garbage, print the old value by luck (if the memory hasn't been reused yet), or crash, and the exact outcome can differ by compiler, optimization level, and what else is on the stack. Note that line 7 itself is legal C++ — binding a reference to a temporary would normally extend a temporary's lifetime, but that extension does not apply here because the dangling reference was already produced inside firstWord, before the caller ever sees it. The fix is to return std::string by value (not by reference); modern C++'s move semantics and guaranteed copy elision (C++17) make that no more expensive than the broken reference version, so there's no performance reason to reach for the reference here.
Circle overrides Shape::area(). This code always prints the base class's area(), never the derived override. Which line causes it?#
1| struct Shape {
2| virtual double area() const { return 0.0; }
3| };
4| struct Circle : Shape {
5| double radius;
6| explicit Circle(double r) : radius(r) {}
7| double area() const override { return 3.14159 * radius * radius; }
8| };
9| void printArea(Shape s) {
10| std::cout << s.area() << std::endl;
11| }
12| Circle c(2.0);
13| printArea(c);Options
Show answer
Line 9 — printArea takes Shape by value, so passing a Circle copies only the Shape base subobject ('object slicing'), losing the derived part and its vtable
Polymorphism through virtual functions only works through a pointer or reference to the base class, because dispatch relies on the object's vptr pointing at its actual (dynamic) type's vtable. printArea(Shape s) takes its parameter by value, so the call printArea(c) invokes Shape's copy constructor with a Circle argument — and a Shape copy constructor only knows how to copy the Shape subobject. The radius member and the Circle vtable pointer are left behind entirely; s inside printArea is a genuine, fully-formed Shape, not a truncated Circle, so s.area() correctly (if surprisingly) calls Shape::area(). This is 'object slicing.' The fix is to take the parameter by reference or pointer (void printArea(const Shape& s)), which binds to the existing Circle object instead of copying it, preserving its vtable and dynamic type.
In C++17, if an exception is thrown during a constructor's member-initializer list after some non-static data members have already been initialized, those already-initialized members (and already-constructed base-class subobjects) are destroyed in reverse order of construction as part of stack unwinding before the exception propagates to the caller.#
Options
Show answer
True. When a constructor throws, the C++ runtime destroys every already-constructed subobject — base classes and non-static data members — in reverse order of construction during stack unwinding. The class's own destructor body is not called because the object never fully constructed, but its initialized members are cleaned up, which is the RAII guarantee.
The C++ standard guarantees that when a constructor exits via an exception, every subobject (base classes and non-static data members) that has already been constructed is destroyed in reverse order of construction. This is the core RAII guarantee: partially constructed objects never leave their already-constructed parts undestroyed. The destructor body of the throwing class itself is NOT invoked (since the object never fully constructed), but the sub-object destructors are.
When std::make_shared<T>(args...) creates a std::shared_ptr<T>, the control block and the managed object are co-located in a _____ heap allocation. A consequence is that the object's _____ is not reclaimed until every std::weak_ptr referencing that control block is destroyed, even though the object's destructor runs when the strong reference count reaches zero. When std::shared_ptr<T>(new T(args...)) is used instead, the object and the control block occupy _____ allocations, so the object's storage is freed immediately when the strong count hits zero, while the control block may outlive it for outstanding weak_ptrs.#
Show answer
When std::make_shared<T>(args...) creates a std::shared_ptr<T>, the control block and the managed object are co-located in a single heap allocation. A consequence is that the object's storage is not reclaimed until every std::weak_ptr referencing that control block is destroyed, even though the object's destructor runs when the strong reference count reaches zero. When std::shared_ptr<T>(new T(args...)) is used instead, the object and the control block occupy separate allocations, so the object's storage is freed immediately when the strong count hits zero, while the control block may outlive it for outstanding weak_ptrs.
This matters when T holds a large amount of memory or sensitive resources: with make_shared, that memory lingers as long as any weak_ptr survives.
std::make_shared performs a single heap allocation that contains both the control block (strong/weak counts, deleter, allocator) and the managed object. When the strong count hits zero the destructor runs, but the memory for the object cannot be returned to the allocator because it lives inside the same block as the control block, which must survive for any outstanding weak_ptr. Only when the weak count also reaches zero does the allocator free the combined block. In contrast, std::shared_ptr<T>(new T) performs two allocations: one for the object (via new) and one for the control block. When the strong count hits zero the object's memory is freed immediately (via delete), and the control block is freed separately when the weak count reaches zero.
A class T derives publicly from std::enable_shared_from_this<T>. Calling shared_from_this() on an instance that is not yet owned by any std::shared_ptr throws an exception of type _____.#
Show answer
A class T derives publicly from std::enable_shared_from_this<T>. Calling shared_from_this() on an instance that is not yet owned by any std::shared_ptr throws an exception of type std::bad_weak_ptr.
This occurs because shared_from_this() internally calls lock on a std::weak_ptr<T> member whose state is still empty — the shared_ptr constructor that would initialize it (when it detects the enable_shared_from_this base class) has never been invoked, so the weak_ptr has never been populated and the lock yields a null shared_ptr, which the implementation detects and converts into the exception.
std::enable_shared_from_this<T> exposes a shared_from_this() member that works by calling lock() on an internal mutable std::weak_ptr<T> member (named weak_this in typical implementations). That weak_ptr is populated only when a shared_ptr constructor takes ownership of the object — the constructor detects the enable_shared_from_this base (via a is_convertible trait check) and, if the internal weak_ptr is empty, stores a weak_ptr aliasing the newly created control block. If no shared_ptr has ever owned the object, weak_this remains default-constructed (empty), so lock() returns a null shared_ptr. The standard-library implementation observes this null result and throws std::bad_weak_ptr.
During std::vector<T>::push_back, if the capacity is exceeded a reallocation must transfer all existing elements from the old buffer to the new buffer. The push_back operation provides a strong exception guarantee (if an exception is thrown, the vector is unchanged). Explain precisely how std::move_if_noexcept is used during this transfer, and what property of T's constructors determines whether each element is moved or copied.#
Show answer
During reallocation, each existing element is transferred using std::move_if_noexcept, which yields an rvalue reference (enabling move) only if T's move constructor is declared noexcept or T has no accessible copy constructor; otherwise it yields a const lvalue reference, forcing a copy. This preserves the strong exception guarantee for push_back: if T's move constructor could throw, copying instead means a throw mid-transfer leaves the original buffer intact. If T's move constructor is noexcept, elements are moved for efficiency. The deciding factor is whether T's move constructor is declared noexcept, with the fallback being whether a copy constructor exists.
std::move_if_noexcept<T> returns T&& when is_nothrow_move_constructible_v<T> is true (or when T is not copy-constructible), and const T& otherwise. The vector reallocation logic uses this so that elements whose move could throw are copied, preserving the original buffer untouched until the full transfer succeeds—giving push_back its strong exception guarantee. A noexcept move constructor allows elements to be moved for performance with no risk.
What is the exact output of the following C++17 program? Trace the template argument deduction, reference collapsing, and std::forward behavior for each call to g.#
#include <iostream>
#include <utility>
void h(int& x) { std::cout << "A"; }
void h(const int& x) { std::cout << "B"; }
void h(int&& x) { std::cout << "C"; }
template<typename T>
void g(T&& x) {
h(std::forward<T>(x));
}
int main() {
int a = 1;
const int b = 2;
g(a);
g(b);
g(3);
g(std::move(a));
std::cout << std::endl;
}Show answer
ABCC
g takes a forwarding reference T&&. For g(a), a is an lvalue of type int, so T deduces to int&; reference collapsing makes x an int&, and std::forward<int&>(x) yields an lvalue, selecting h(int&) → A. For g(b), b is an lvalue of type const int, so T deduces to const int&; std::forward<const int&>(x) yields a const int& lvalue, and only h(const int&) is viable → B. For g(3), the prvalue causes T to deduce to int; x is int&& and std::forward<int>(x) yields an xvalue, selecting h(int&&) → C. For g(std::move(a)), std::move(a) is an xvalue, so T again deduces to int; std::forward<int>(x) yields an xvalue, selecting h(int&&) → C. The output is ABCC.
Related interview questions
Job market
See cpp salaries and hiring demand from live job postings.
The other 71 questions
This page shows 25 and marks what you pick. That's as far as a page can go. A free account opens the other 71 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.
Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan