C++ Interview Questions and Answers
26 questions8 beginner · 11 intermediate · 7 advancedLast reviewed
This set is for candidates preparing for C++ roles, from graduate screens to experienced backend, systems and embedded interviews. It concentrates on the topics interviewers keep returning to: ownership and lifetime, the object model, move semantics, the standard library, and the situations where the language gives you undefined behaviour instead of an error.
To practise, cover the answer and say yours aloud first, ideally with a tiny example from memory. Then compare, and note any point you missed. Snippets are kept short and conventional on purpose; type them into a compiler yourself and vary them. If a topic is new, start with the C++ course, and revisit object-oriented programming for the polymorphism questions.
1What is the difference between a pointer and a reference in C++?Beginner
A pointer is a variable that holds an address. It can be null, can be reassigned to point elsewhere, and supports pointer arithmetic. A reference is an alias for an existing object: it must be bound when it is created, cannot be reseated afterwards, and in well-formed code is never null.
That makes references a good fit for required, non-owning parameters (
const Order&to read without copying), and pointers a fit for optional or reseatable relationships. The classic trap is assignment:r = bon a reference does not rebind it, it assigns through it to the original object. Neither says anything about ownership, which is why raw pointers should not be used to own memory in modern code.cpp int main() { int a = 1, b = 2; int* p = &a; p = &b; // fine: a pointer can be reseated, or set to nullptr int& r = a; // a reference must bind at initialisation r = b; // copies b's value into a; r still refers to a, so a is now 2 }What the interviewer is checking: Whether you know the practical differences, including that assigning to a reference never rebinds it.
Likely follow-up: When would you pass a function argument as a pointer instead of a reference?
2Explain stack versus heap memory in C++. Where does each kind of object live?Beginner
Local variables have automatic storage, usually on the stack. Allocation is nearly free, and the object is destroyed when its scope ends. The stack is small, so a huge local array or very deep recursion can overflow it. Heap (free store) memory is requested at run time with
newor by containers, lives until it is released, and costs more to allocate, but its size and lifetime are not tied to a scope.A
std::vectorobject may sit on the stack while its elements live on the heap. My default is stack objects and standard containers, and the heap when the size is unknown at compile time, the object must outlive the function, or I need polymorphic ownership. Returning a pointer or reference to a stack local is a classic bug, because the object is already gone.What the interviewer is checking: Whether you can describe lifetime and cost differences and name the hazards of each, not just where the bytes sit.
Likely follow-up: What actually happens if a function returns the address of one of its local variables?
3What is the difference between new/delete and malloc/free? Can you mix them?Beginner
newallocates memory and runs the constructor, returning a correctly typed pointer; on failure it throwsstd::bad_alloc.deleteruns the destructor and then releases the memory.malloconly returns raw bytes asvoid*(or null on failure), andfreereleases them without running any destructor, so they are wrong for objects with non-trivial construction.You must pair them correctly:
newwithdelete,new[]withdelete[],mallocwithfree. Mixing them is undefined behaviour, because the allocator may track blocks differently and destructors may be skipped. In modern code I avoid rawnewanddeletealtogether and usestd::make_unique,std::make_sharedand containers, so releasing memory is automatic.What the interviewer is checking: Whether you know that constructors and destructors are the real difference, and that mismatching forms is undefined.
Likely follow-up: Why is it a bug to release an array allocated with new[] using plain delete?
4What is RAII, and why is it central to C++?Beginner
RAII (Resource Acquisition Is Initialisation) ties a resource's lifetime to an object's lifetime: acquire the resource in the constructor, release it in the destructor. Because destructors of local objects run on every exit from a scope, including a
returnor a thrown exception during stack unwinding, cleanup happens withoutfinallyblocks or manual calls.It covers more than memory:
std::unique_ptrfor heap objects,std::lock_guardfor mutexes,std::ifstreamfor files, and containers all use it. The snippet shows a lock that is released on any exit path. Two cautions: destructors must not throw, and a class that owns a resource needs its copy and move behaviour considered (the rule of five).cpp #include <mutex> std::mutex m; int hits = 0; void record() { std::lock_guard<std::mutex> lock(m); ++hits; // the mutex is released when lock goes out of scope, even if this threw }What the interviewer is checking: Whether you see RAII as the answer to resource safety in general, not just a memory trick.
Likely follow-up: How would you wrap a C library handle, such as a file descriptor, in an RAII class?
5In what order are constructors and destructors called for a derived class that has members?Beginner
Construction goes base class first, then data members in the order they are declared in the class, then the body of the derived constructor. Destruction runs in exactly the reverse order: derived body, members in reverse declaration order, then the base.
Two details matter. Members are initialised in declaration order, not in the order written in the initialiser list, so a list that disagrees can read an uninitialised member. And if you declare any constructor, the compiler stops generating the default one. The program below prints Vehicle, Engine, Truck on construction and the mirror image on destruction.
cpp #include <iostream> struct Engine { Engine() { std::cout << "Engine ctor\n"; } ~Engine() { std::cout << "Engine dtor\n"; } }; struct Vehicle { Vehicle() { std::cout << "Vehicle ctor\n"; } ~Vehicle() { std::cout << "Vehicle dtor\n"; } }; struct Truck : Vehicle { Engine engine; Truck() { std::cout << "Truck ctor\n"; } ~Truck() { std::cout << "Truck dtor\n"; } }; int main() { Truck t; }Output
Vehicle ctor Engine ctor Truck ctor Truck dtor Engine dtor Vehicle dtor
What the interviewer is checking: Whether you know the full order, including members, and why reverse destruction order makes cleanup safe.
Likely follow-up: What happens if you call a virtual function from inside a base-class constructor?
6What does const correctness mean? What is the difference between
const int*andint* const?BeginnerConst correctness means marking everything that should not change as
const, so the compiler enforces it. A member function declaredconstpromises not to modify the object's observable state and is the only kind you can call on a const object. Large read-only parameters are passed asconst T&to avoid a copy without allowing modification.Read pointer declarations right to left.
const int*is a pointer to a const int: you can repoint it, but not change the int through it.int* constis a const pointer to an int: you can change the int, but not repoint it.mutablemarks a member, such as a cache or a mutex, that may change even inside a const function. Retrofitting const into an existing codebase is painful, so start with it.What the interviewer is checking: Whether you can read compound declarations correctly and explain why const member functions matter.
Likely follow-up: Why would a class provide both a const and a non-const overload of an accessor such as
operator[]?7Why prefer the member initialiser list over assigning members in the constructor body?Beginner
All members are initialised before the constructor body runs. If you write assignments in the body, a class-type member is first default-constructed and then assigned, which is wasted work, for example building an empty string and then overwriting it. With an initialiser list, the member is constructed directly with the value you want.
For some members the list is not optional:
constmembers, reference members, and members of a type with no default constructor can only be initialised there. Also remember that initialisation follows declaration order, not list order, so keep them consistent; compilers warn when they differ. Base-class constructors with arguments are likewise called from the list.What the interviewer is checking: Whether you understand that construction happens before the body, not only that the list is a style preference.
Likely follow-up: What does
explicitdo on a single-argument constructor, and when would you use it?8Compare a raw array, std::array and std::vector. Which is your default and why?Beginner
A raw array has a fixed size known at compile time, decays to a pointer when passed to a function (losing its length), and has no bounds checking.
std::arraywraps the same fixed-size storage, with no heap allocation, but remembers its size and works with the standard algorithms.std::vectoris a resizable array: its elements are contiguous but stored on the heap.My default is
std::vector, because most sizes are not fixed. I usestd::arraywhen the size is truly constant and I want no allocation. Both offerat(), which checks bounds and throwsstd::out_of_range, whileoperator[]is unchecked, so an out-of-range index is undefined behaviour rather than an error.What the interviewer is checking: Whether you can choose a container by its trade-offs and know the unchecked nature of operator[].
Likely follow-up: Why is it unsafe to keep a pointer to a vector's first element across a push_back?
9When do you use unique_ptr, and what makes it different from a raw pointer?Intermediate
std::unique_ptrexpresses sole ownership of a heap object. It deletes the object automatically when it goes out of scope, so there is no leak on early returns or exceptions. It cannot be copied, only moved, which is how ownership is transferred; the source becomes null. With the default deleter it is typically the same size as a raw pointer and has no meaningful runtime overhead.I use it as the default owner, for members that own a polymorphic object, and as a factory return type. Create it with
std::make_unique. For code that merely uses the object without owning it, pass a reference or a raw pointer taken withget(), so the function signature shows that ownership does not change.cpp #include <iostream> #include <memory> int main() { auto a = std::make_unique<int>(7); auto b = std::move(a); // ownership moves to b std::cout << (a == nullptr) << " " << *b << "\n"; }Output
1 7
What the interviewer is checking: Whether you treat ownership as part of the interface and know that unique_ptr is move-only.
Likely follow-up: How do you store a unique_ptr in a std::vector, and what does that imply for copying the vector?
10How does shared_ptr work, and what does it cost compared with unique_ptr?Intermediate
std::shared_ptrallows several owners. Each copy shares a control block holding a reference count; when the last owner is destroyed, the object is deleted. The counts are updated atomically, so copying and destroying shared_ptr instances is safe across threads.That convenience has a cost: an extra control block (allocated together with the object when you use
std::make_shared), atomic increments and decrements, and a pointer that is twice the size. It also blurs who owns what, which makes lifetimes harder to reason about, and it can leak through cycles. So I start withunique_ptrand reach forshared_ptronly when ownership is genuinely shared. Never build two shared_ptrs from the same raw pointer; each would think it is the sole owner and delete the object twice.What the interviewer is checking: Whether you can describe the control block and the trade-offs instead of treating shared_ptr as a free upgrade.
Likely follow-up: What is enable_shared_from_this for, and what goes wrong if you call shared_from_this too early?
11How can shared_ptr leak memory, and how does weak_ptr solve it?Intermediate
If two objects hold
shared_ptrs to each other, each keeps the other's reference count above zero, so neither is ever destroyed even after your code lets go of both. That reference cycle is a leak, because the counts never reach zero.std::weak_ptris a non-owning observer: it does not increase the strong count. To use the object you calllock(), which returns ashared_ptr, empty if the object is already gone, so the check and the access are one step. Make the back-link or observer edge weak, such as a child's pointer to its parent. In the snippet,nextowns butprevdoes not, so both nodes are freed at the end ofmain.cpp #include <memory> struct Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // weak back-link breaks the cycle }; int main() { auto a = std::make_shared<Node>(); auto b = std::make_shared<Node>(); a->next = b; b->prev = a; } // both nodes are destroyed hereWhat the interviewer is checking: Whether you understand why the cycle leaks and can pick the right edge to make weak.
Likely follow-up: Why is checking expired() and then using the object less safe than calling lock()?
12How do virtual functions work under the hood? What is a vtable?Intermediate
A virtual call is resolved at run time using the object's dynamic type. The C++ standard does not mandate how, but mainstream compilers use a vtable: one table of function pointers per polymorphic class, and a hidden pointer (the vptr) inside each object that points to its class's table. A virtual call loads the vptr, indexes the table and jumps through the pointer.
The costs are one pointer of space per object, an indirect call, and often lost inlining. Always write
overrideso the compiler catches signature mismatches. One famous trap: inside a constructor or destructor the object is only the class currently being built or torn down, so a virtual call there dispatches to that class's version, not the most-derived one. Calls through an object (not a pointer or reference) are resolved statically.What the interviewer is checking: Whether you know the usual implementation and the practical consequences, including the constructor pitfall.
Likely follow-up: Why can't a constructor be virtual, but a destructor must often be?
13In this snippet a Camera is deleted through a Sensor* with a non-virtual destructor. What is wrong, and how do you fix it?Intermediate
Sensorhas no virtual destructor, yet aCamerais deleted through aSensor*. Deleting a derived object through a pointer to a base class whose destructor is not virtual is undefined behaviour. In practiceCamera's destructor may never run, so its frame buffer leaks, or worse.The fix is to declare
virtual ~Sensor() = default;in the base class. The rule: any class designed to be used polymorphically, or deleted through a base pointer, needs a virtual destructor. The cost is a vtable pointer, which a polymorphic class already pays for. If instances are never deleted through the base, a protected non-virtual destructor is the alternative, which also stops callers from attempting it.cpp struct Sensor { ~Sensor() {} // not virtual }; struct Camera : Sensor { ~Camera() { /* release the frame buffer */ } }; int main() { Sensor* s = new Camera; delete s; // undefined behaviour: Camera's destructor may not run }What the interviewer is checking: Whether you spot the bug at once and state both the rule and the consequence.
Likely follow-up: If a class has no virtual functions at all, should it still get a virtual destructor?
14Explain copy versus move semantics. What does std::move actually do?Intermediate
Copying creates an independent duplicate of a resource, such as a new buffer and a copy of every element. Moving transfers the resource to the new object and leaves the source valid but in an unspecified state. For a
std::vector, a move steals the internal pointer in O(1) instead of copying every element in O(n).std::movedoes not move anything: it is a cast that turns its argument into an rvalue reference, which allows the move constructor or move assignment to be selected. The actual transfer happens in those functions. Pitfalls: moving from aconstobject silently falls back to a copy, and you should not rely on the value of a moved-from object, only assign to it or destroy it. Do not writestd::moveon a returned local.What the interviewer is checking: Whether you know that std::move is only a cast and can name where moves silently fail to happen.
Likely follow-up: What is the difference between an lvalue and an rvalue, and why do rvalues bind to the move overload?
15Explain the rule of three, the rule of five and the rule of zero.Intermediate
The rule of three says that if a class needs a custom destructor, copy constructor or copy assignment operator, it almost certainly needs all three, usually because it owns a raw resource. The rule of five adds the move constructor and move assignment operator. Declaring a destructor or a copy operation also stops the compiler generating the moves, so copies silently take over.
The rule of zero is the goal: give a class members that manage themselves, such as
std::string,std::vectorandstd::unique_ptr, and then declare none of the five, since the generated ones do the right thing. Write them by hand only when wrapping a raw resource. If you need to opt out, use= delete, for example to make a type non-copyable.What the interviewer is checking: Whether you can explain why the rules exist and prefer the rule of zero over hand-written special members.
Likely follow-up: What does the compiler-generated copy constructor do for a class that holds a raw pointer member?
16What are templates, and what happens when the compiler sees one used?Intermediate
A template is code parameterised by types or values, so one definition works for many types with no run-time overhead. When you use it, for instance
largest<int>, the compiler instantiates a separate concrete version for those arguments and type-checks it then, which is why errors can be long and appear at the call site.Because the compiler needs the full body to instantiate, template definitions normally live in headers. Function template arguments are usually deduced:
largest(3, 9)givesint, butlargest(3, 2.5)fails because the two arguments deduce different types for oneT. Compared with virtual functions, templates give static polymorphism: faster, but resolved at compile time and possibly bloating the binary. Since C++20, concepts let you state what a type must support.cpp template <typename T> T largest(const T& a, const T& b) { return (a < b) ? b : a; } // largest(3, 9) -> T deduced as int // largest(3, 2.5) -> error: conflicting deductions for TWhat the interviewer is checking: Whether you understand instantiation, why definitions live in headers, and how deduction can fail.
Likely follow-up: How does template specialisation differ from overloading, and when would you need it?
17How does std::vector grow, and what should you know about capacity versus size?Intermediate
size()is how many elements are stored;capacity()is how many fit before a new allocation is needed. Whenpush_backexceeds capacity, the vector allocates a larger block, moves or copies the elements across and frees the old one. The growth is geometric, though the exact factor is implementation-defined, which makespush_backamortised O(1).Reallocation invalidates all iterators, pointers and references to elements, so holding
&v[0]across a push_back is a bug. If you know the final size, callreserve(n)once to avoid repeated reallocations and keep references stable until you exceed that capacity.emplace_backbuilds the element in place from constructor arguments rather than constructing a temporary and moving it.cpp #include <vector> int main() { std::vector<int> readings; readings.reserve(1000); // one allocation up front for (int i = 0; i < 1000; ++i) readings.push_back(i); // no reallocation happens }What the interviewer is checking: Whether you know the amortised reasoning and the invalidation consequences of reallocation.
Likely follow-up: Does vector::clear() release the memory? How would you actually shrink it?
18std::map versus std::unordered_map: how do they differ, and which do you choose?Intermediate
std::mapis an ordered associative container, typically implemented as a balanced tree. Operations are O(log n), keys iterate in sorted order, and it needs a strict weak ordering (operator<).std::unordered_mapis a hash table: average O(1) lookup, O(n) in the worst case, no ordering, and it needs a hash function plus equality.I choose
unordered_mapfor plain key-to-value lookups andmapwhen I need sorted iteration or range queries such aslower_bound. A common trap isoperator[]: on a missing key it inserts a default-constructed value, so using it just to test existence changes the container. Usefind, orcontainsin C++20. A rehash in an unordered_map invalidates iterators, but not references to its elements.What the interviewer is checking: Whether you pick by required operations and know the operator[] insertion side effect.
Likely follow-up: What do you need to provide to use your own struct as a key in an unordered_map?
19Which operations invalidate iterators in the standard containers? Show a safe way to erase while looping.Intermediate
It depends on the container. For
vector, an insertion that reallocates invalidates everything; otherwise insertion and erasure invalidate iterators at and after the change point. Forlistandmap, insertion invalidates nothing and erasure invalidates only the erased element. Forunordered_map, a rehash invalidates iterators.A frequent bug is erasing inside a loop and then incrementing the dead iterator. Use the return value instead:
erasereturns an iterator to the next element, so only advance manually when you did not erase. Another is callingpush_backinside a range-based for over the same vector. For removing by condition, the erase-remove idiom (orstd::erase_ifin C++20) is cleaner than a hand-written loop.cpp #include <vector> int main() { std::vector<int> v = {1, 2, 3, 4, 5, 6}; for (auto it = v.begin(); it != v.end();) { if (*it % 2 == 0) it = v.erase(it); // returns the next valid iterator else ++it; } // v now holds {1, 3, 5} }What the interviewer is checking: Whether you know per-container rules and use erase's return value rather than reusing a dead iterator.
Likely follow-up: What does the erase-remove idiom do, and why does remove alone not shrink the vector?
20What is undefined behaviour? Give examples and explain why it is dangerous.Advanced
Undefined behaviour means the standard places no requirements on what the program does: it may crash, appear to work, or do something strange. Examples: reading past the end of an array, dereferencing null or a dangling pointer, signed integer overflow, using an uninitialised variable, a data race, deleting through a base pointer without a virtual destructor, and a double delete.
The danger is that compilers assume it never happens and optimise on that basis. A check like
if (x + 1 < x)on a signed int may be removed entirely, and a null check placed after a dereference may be dropped. So a bug can show up only in optimised builds. Unsigned overflow, by contrast, is defined: it wraps. Defence: warnings,-fsanitize=address,undefinedin testing, bounds-checked access, and RAII types instead of raw pointers.What the interviewer is checking: Whether you understand that UB allows compiler optimisation, not just crashes, and know practical defences.
Likely follow-up: How do undefined, unspecified and implementation-defined behaviour differ?
21What is object slicing, and how do you avoid it?Advanced
Slicing happens when a derived object is copied into a base-class object by value. Only the base part is copied; the derived members and the derived type are lost, so virtual calls on the copy go to the base version. The usual cause is a function that takes
Animalby value or a container such asstd::vector<Animal>that stores objects of the base type.To keep polymorphism, pass by reference or pointer, and store smart pointers like
std::vector<std::unique_ptr<Animal>>. You can also make the base class abstract or delete its copy operations so accidental slicing fails to compile. In the snippet, the by-value call prints the base sound, while the reference call prints the derived one.cpp #include <iostream> #include <string> struct Animal { virtual std::string sound() const { return "..."; } virtual ~Animal() = default; }; struct Dog : Animal { std::string sound() const override { return "woof"; } }; void byValue(Animal a) { std::cout << a.sound() << "\n"; } void byRef(const Animal& a) { std::cout << a.sound() << "\n"; } int main() { Dog d; byValue(d); byRef(d); }Output
... woof
What the interviewer is checking: Whether you see how value semantics and polymorphism interact, and can name the standard remedies.
Likely follow-up: How would you copy a polymorphic object through a base pointer without knowing its derived type?
22Why should move operations be noexcept? What happens to vector reallocation if they are not?Advanced
When a
std::vectorreallocates, it must keep its strong guarantee: if something throws midway, the original vector must be unchanged. Moving elements into new storage and then throwing would leave the old ones damaged. So the library usesstd::move_if_noexcept: it moves only if the move constructor isnoexcept, or if the type is not copyable; otherwise it copies.So forgetting
noexcepton a move constructor can silently turn every growth step into a deep copy, a large hidden performance cost. Mark move constructors and move assignmentnoexceptwhenever they only swap pointers, as in the snippet. Destructors are implicitlynoexcept, and one that throws during stack unwinding ends the program viastd::terminate.cpp #include <cstddef> class Buffer { int* data_; std::size_t size_; public: explicit Buffer(std::size_t n) : data_(new int[n]()), size_(n) {} Buffer(const Buffer&) = delete; Buffer& operator=(const Buffer&) = delete; Buffer(Buffer&& o) noexcept : data_(o.data_), size_(o.size_) { o.data_ = nullptr; o.size_ = 0; } Buffer& operator=(Buffer&& o) noexcept { if (this != &o) { delete[] data_; data_ = o.data_; size_ = o.size_; o.data_ = nullptr; o.size_ = 0; } return *this; } ~Buffer() { delete[] data_; } };What the interviewer is checking: Whether you connect exception guarantees to a real performance consequence in the standard library.
Likely follow-up: Which special member functions does the compiler generate for this class if you remove the deleted copy operations?
23Describe common ways to end up with a dangling reference or pointer in C++.Advanced
A dangling reference refers to an object whose lifetime has ended. The textbook case is returning a reference or pointer to a local variable. Others: holding an iterator, pointer or reference to a vector element across an operation that reallocates; keeping a
std::string_vieworconst char*taken from a temporary string that has since been destroyed; and a lambda that captures a local by reference but outlives the function, for example when handed to a thread or stored as a callback.Using any of them is undefined behaviour, and it often appears to work until memory is reused. I avoid it by returning values (moves and elision make that cheap), capturing by value for deferred work, keeping views no longer-lived than their source, and running tests under AddressSanitizer, which detects use-after-free and use-after-return when enabled.
What the interviewer is checking: Whether you can list the realistic lifetime traps beyond the textbook example and name a tool for finding them.
Likely follow-up: Why is
std::string_view v = std::string("abc");a problem, and what would be a safe version?24Should you write
return std::move(local);to make a return cheaper?AdvancedNo, it usually makes things worse. Returning a local by name already lets the compiler elide the copy entirely (named return value optimisation, common but not guaranteed), and when it does not elide, the local is treated as an rvalue and moved automatically. Since C++17, returning a prvalue, such as a freshly built temporary, is guaranteed to construct directly in the caller's object.
Wrapping the local in
std::movechanges the expression type, which can disable the elision and force a move that was not needed. Compilers often warn about it. The rule is to return locals by value plainly and trust elision and implicit move. Explicitstd::moveis right when you return something that is not a plain local, such as a data member of an object you are emptying.cpp #include <vector> std::vector<int> makeReadings() { std::vector<int> r(1000); return r; // elided or implicitly moved: no copy // return std::move(r); // pessimisation: can disable elision }What the interviewer is checking: Whether you know modern copy elision rules and resist cargo-cult std::move.
Likely follow-up: When is returning by const value, such as
const Widget make(), a bad idea?25Is shared_ptr thread-safe? What exactly is and is not protected?Advanced
Only the control block is. The reference counts are atomic, so different threads can each copy, pass around and destroy their own
shared_ptrinstances that share one object, and the object is deleted exactly once. That is the guarantee, and it is narrower than people assume.Two things are not protected. The pointed-to object itself: concurrent reads and writes to it still need a mutex or other synchronisation. And a single
shared_ptrinstance: one thread reassigning or resetting it while another reads that same variable is a data race. For that, use a lock, orstd::atomic<std::shared_ptr<T>>since C++20. A common bug is believing that a shared_ptr makes the data safe to modify from several threads.What the interviewer is checking: Whether you separate the control block's guarantee from the object's and the pointer variable's.
Likely follow-up: Where does a data race on a shared_ptr variable show up in code that publishes a configuration object to worker threads?
26What is a forwarding reference, and how do std::forward and std::move differ?Advanced
In a function template like
template <typename T> void add(T&& x), whereTis deduced,T&&is a forwarding reference, not an rvalue reference: it binds to lvalues and rvalues, andTrecords which one was passed. Inside the function the namexis always an lvalue, even if it was bound to an rvalue.std::move(x)unconditionally casts to an rvalue.std::forward<T>(x)casts to an rvalue only if the original argument was an rvalue, so the callee sees the same value category the caller used (perfect forwarding). That is how wrappers such asemplace_backandmake_uniquepass constructor arguments through without extra copies. Not every&&forwards:Widget&&andconst T&&are plain rvalue references, and so isT&&whenTis a class template parameter rather than deduced for the call.What the interviewer is checking: Whether you can distinguish a forwarding reference from an rvalue reference and explain why forward exists.
Likely follow-up: Why does a function that takes
Widget&& wstill needstd::move(w)to pass it on as an rvalue?
Fill the gaps with a free course
If one of these C++ answers felt shaky, these course covers the same ground step by step: C++.
More interview questions
- Java
- Spring Boot
- Python
- JavaScript
- React
- Node.js
- Data Structures & Algorithms
- SQL & Databases
- System Design
- DevOps
- AWS & Cloud
- Security
- Generative AI & LLMs
- Machine Learning
- Behavioral & HR
Practising C++ for a real opening? Upload your resume to see which open jobs list C++ skills you already have, or browse open roles.