Skip to content

C++ Software Engineer Interview Questions & Answers

40 questions for C++ Software Engineer interviews — 3 easy, 32 medium, 5 hard.

About the C++ Software Engineer role

A C++ software engineer designs and builds features for large products such as browsers, databases, networking software, CAD tools and cloud infrastructure. They own modules end to end, write modern C++ with smart pointers and templates, review code and improve performance. Interviews combine data structures and algorithms, object-oriented design, modern C++ questions and debugging scenarios. Engineers with two to five years of experience commonly hold this title.

Modern C++17/20 Templates Code review CMake builds Performance tuning
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

What is std::string_view and what are its pitfalls?

Save to ▾
Show answer
string_view is a non-owning pointer and length pair that refers to existing characters, so passing it avoids copies and accepts both std::string and literals. The main pitfall is lifetime: it must not outlive the data it views, so returning a string_view to a local string or a temporary dangles. It is also not guaranteed to be null-terminated, so do not pass data() to C functions expecting C strings.
02

After calling std::move on a string, a teammate prints it and sees an empty value. Is this a bug?

Save to ▾
Show answer
It is a misunderstanding. A moved-from standard library object is in a valid but unspecified state; for std::string it is often empty, but you cannot rely on that. The safe operations are destroying it or assigning a new value. Reading it is not undefined behaviour, but the content is meaningless. Clang-tidy's bugprone-use-after-move check catches this pattern in code review.
03

What is a deadlock and how do you prevent it?

Save to ▾
Show answer
A deadlock happens when threads wait forever for each other's locks, for example thread A holds m1 and waits for m2 while thread B holds m2 and waits for m1. It needs mutual exclusion, hold and wait, no preemption and circular wait. Prevent it by always locking in a fixed global order, locking several mutexes together with std::scoped_lock, keeping critical sections small and never calling unknown code while holding a lock.
04

A function taking a Money object is silently called with an int and gives wrong results. What is happening?

Save to ▾
Show answer
Money probably has a single-argument constructor such as Money(int paise) that is not marked explicit, so the compiler performs an implicit conversion from int. A call like pay(500) compiles even though the unit is unclear. Mark single-argument constructors explicit, so callers must write pay(Money(500)). Also consider strong types or named factory functions like Money::fromRupees to make units obvious.
05

Write a variadic function that sums any number of arguments and one that prints them.

Save to ▾
Show answer
#include <iostream> template <typename... Args> auto sum(Args... args) { return (args + ...); // unary right fold } template <typename... Args> void printAll(const Args&... args) { ((std::cout << args << ' '), ...); // comma fold std::cout << '\n'; } // sum(1, 2, 3.5) returns 6.5; printAll(1, 'x', 2.5) prints 1 x 2.5
06

Why are templates usually defined in header files?

Save to ▾
Show answer
A template is only a pattern; code is generated when it is instantiated with concrete types, and the compiler must see the full definition at that point. If the body lives in a .cpp file, other translation units cannot instantiate it and you get linker errors. The usual fix is defining templates in headers, or using explicit instantiation in the .cpp for a known, fixed set of types.
07

Should a Car class inherit from Engine or contain an Engine? Explain your choice.

Save to ▾
Show answer
A car is not an engine, it has an engine, so composition is correct: class Car holds an Engine member or a unique_ptr<Engine>. Inheritance models an is-a relationship and exposes the base interface, which would let callers treat a Car as an Engine. Composition also allows swapping engine types at run time through an interface such as PetrolEngine or ElectricMotor and keeps coupling low.
08

What is the erase-remove idiom and what replaced it in C++20?

Save to ▾
Show answer
std::remove does not delete elements; it shifts the kept elements forward and returns an iterator to the new logical end. You then call erase to shrink the container: v.erase(std::remove(v.begin(), v.end(), 0), v.end());. remove_if works the same with a predicate. C++20 adds std::erase(v, value) and std::erase_if(v, pred), which do both steps in one call.
09

Write a custom hash so a struct can be used as a key in std::unordered_map.

Save to ▾
Show answer
#include <functional> #include <string> #include <unordered_map> struct Point { int x, y; bool operator==(const Point&) const = default; }; struct PointHash { std::size_t operator()(const Point& p) const noexcept { std::size_t h1 = std::hash<int>{}(p.x); std::size_t h2 = std::hash<int>{}(p.y); return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); } }; std::unordered_map<Point, std::string, PointHash> grid;
10

Show the copy-and-swap idiom and explain why it gives strong exception safety.

Save to ▾
Show answer
class Buffer { std::size_t n_; int* p_; public: explicit Buffer(std::size_t n) : n_(n), p_(new int[n]{}) {} Buffer(const Buffer& o) : n_(o.n_), p_(new int[o.n_]) { std::copy(o.p_, o.p_ + n_, p_); } ~Buffer() { delete[] p_; } friend void swap(Buffer& a, Buffer& b) noexcept { std::swap(a.n_, b.n_); std::swap(a.p_, b.p_); } Buffer& operator=(Buffer other) noexcept { // copy made before we touch *this swap(*this, other); return *this; } }; // If the copy throws, *this is unchanged; self-assignment is safe too.
11

An asynchronous callback crashes intermittently. It is a lambda that captures local variables by reference. Explain the problem and fix.

Save to ▾
Show answer
The lambda runs later on another thread or event loop, after the function that created it has returned, so the captured references point to destroyed stack variables. Fix it by capturing by value, moving owned data into the lambda with init captures, or capturing a shared_ptr or weak_ptr for objects whose lifetime must be extended or checked. Avoid default [&] for any callback that can outlive the scope.
12

Explain the rule of three, rule of five and rule of zero.

Save to ▾
Show answer
Rule of three: if a class needs a custom destructor, copy constructor or copy assignment, it usually needs all three because it manages a resource. Rule of five adds the move constructor and move assignment from C++11. Rule of zero says the best design avoids writing any of them by holding resources in members such as std::vector, std::string and unique_ptr, which already manage themselves.
13

When do you need the typename keyword inside a template?

Save to ▾
Show answer
Inside a template, a name that depends on a template parameter, such as T::value_type, could be a type or a static member, so the compiler assumes it is a value. You must write typename T::value_type x; to say it is a type. Similarly, a dependent member template needs the template keyword, as in obj.template get<0>(). C++20 relaxes this in some contexts, but the rule still matters.
14

How would you choose between exceptions, error codes and std::expected for error handling?

Save to ▾
Show answer
Exceptions suit rare, truly exceptional failures, keep the happy path clean and work well with RAII, but have unpredictable cost when thrown and are often disabled in embedded or game code. Error codes are explicit and cheap but easy to ignore. C++23 std::expected<T, E> returns either a value or an error, combining explicit handling with clean types. I follow the project convention and use expected for expected failures like parsing.
15

Why should a base class have a virtual destructor?

Save to ▾
Show answer
If you delete a derived object through a base pointer and the base destructor is not virtual, only the base destructor runs, which is undefined behaviour and typically leaks the derived part's resources. Making the destructor virtual ensures the derived destructor runs first, then the base one. Rule: any class meant to be used polymorphically needs a public virtual destructor, or a protected non-virtual one.
16

A program does millions of lookups in a std::map with string keys and is slow. How would you speed it up?

Save to ▾
Show answer
If ordering is not needed, switch to std::unordered_map and call reserve with the expected size to avoid rehashing. For string keys, use heterogeneous lookup with string_view to avoid building temporary strings. If keys are fixed after loading, a sorted vector with binary search is very cache friendly. I would profile before and after, because hashing long strings has its own cost.
17

You need a container that holds ints, strings and doubles together. Which approach would you choose?

Save to ▾
Show answer
If the set of types is fixed and known, std::vector<std::variant<int, std::string, double>> is type-safe and needs no heap per element; std::visit handles each type. If types are open-ended, std::any works but needs any_cast and loses compile-time checks. If the items share behaviour, a base class interface with unique_ptr is cleanest. I usually prefer variant for closed sets.
18

What does noexcept mean and why should move constructors be noexcept?

Save to ▾
Show answer
noexcept promises a function will not throw; if it does, std::terminate is called. Containers rely on this: when std::vector reallocates, it moves elements only if the move constructor is noexcept, otherwise it copies them to keep the strong exception guarantee. So a missing noexcept on a move constructor can silently make vector growth much slower. Destructors are noexcept by default.
19

What is the diamond problem and how does virtual inheritance solve it?

Save to ▾
Show answer
When B and C both inherit from A and D inherits from B and C, D contains two copies of A, so accessing A's members is ambiguous. Declaring class B : virtual public A and class C : virtual public A makes D share a single A subobject. The most-derived class then initialises A directly. Virtual inheritance adds pointer overhead, so it is used sparingly, usually for interface-like bases.
20

What are std::optional, std::variant and std::any?

Save to ▾
Show answer
std::optional<T> holds either a T or nothing, ideal for functions that may not return a value, without using magic values or pointers. std::variant<A, B> is a type-safe union that holds exactly one of the listed types, inspected with std::visit or std::get_if. std::any holds a value of any copyable type and needs any_cast to read it. Prefer optional and variant because they are checked at compile time.
21

What is template argument deduction and class template argument deduction?

Save to ▾
Show answer
For function templates the compiler deduces T from the call, so maxOf(3, 7) deduces int; mixing int and double fails because T would be ambiguous. Class template argument deduction, added in C++17, lets you write std::vector v{1, 2, 3}; or std::pair p(1, 2.5); without spelling the template arguments. Deduction guides can be written to tell the compiler how to deduce types from constructors.
22

What does the C++20 ranges library add?

Save to ▾
Show answer
Ranges let algorithms take whole containers, as in std::ranges::sort(v), instead of begin and end pairs. Views such as filter, transform, take and reverse are lazy and compose with the pipe operator, for example v | std::views::filter(isEven) | std::views::transform(square). Nothing is computed until you iterate. Ranges also use concepts, so errors are clearer, and projections let you sort by a member easily.
23

What is the difference between push_back and emplace_back?

Save to ▾
Show answer
push_back takes an existing object, copying or moving it into the container. emplace_back forwards constructor arguments and builds the element directly in place, which can avoid a temporary. For example v.emplace_back(name, age) constructs a Person inside the vector. The gain is small when you already have an object, and emplace_back can call explicit constructors unexpectedly, so use it deliberately.
24

What is object slicing?

Save to ▾
Show answer
Slicing happens when a derived object is copied into a base object by value. Only the base part is copied, the derived data is lost and virtual calls use the base version. Example: void draw(Shape s) called with a Circle loses Circle's radius and behaviour. Avoid it by passing polymorphic objects by reference or pointer, and consider deleting copy operations in polymorphic base classes.
25

Write a C++20 concept and use it to constrain a function.

Save to ▾
Show answer
#include <concepts> #include <string> template <typename T> concept Printable = requires(T t) { { t.toString() } -> std::convertible_to<std::string>; }; template <Printable T> std::string describe(const T& obj) { return "Item: " + obj.toString(); } struct User { std::string name; std::string toString() const { return name; } }; // describe(User{"Asha"}) compiles; describe(42) fails with a clear message.
26

What kinds of constructors does C++ have?

Save to ▾
Show answer
A default constructor takes no arguments. A parameterised constructor takes arguments to initialise members. A copy constructor, T(const T&), creates an object from another of the same type. A move constructor, T(T&&), steals resources from a temporary. There are also delegating constructors that call another constructor and converting constructors that take one argument; mark those explicit to avoid unwanted implicit conversions.
27

Design a class structure for UPI, card and net banking payments in a checkout system.

Save to ▾
Show answer
I would define an abstract PaymentMethod interface with virtual functions such as authorize, capture, refund and a virtual destructor. UpiPayment, CardPayment and NetBankingPayment implement it with their own validation. Checkout depends only on PaymentMethod, and a factory creates the right object from user choice. New methods such as wallets can be added without touching checkout code, which follows the open-closed principle.
28

How would you explain polymorphism to a junior developer who has just joined your team?

Save to ▾
Show answer
Situation: a new joiner was confused by virtual functions in our codebase. Task: help them become productive quickly. Action: I used a real example from our code, a Notifier base class with Email and Sms versions, drew how the vtable decides the call at run time, and gave them a small task to add a WhatsApp notifier. Result: they finished it in a day and later explained the idea to the next intern.
29

A teammate complains that a template function produces hundreds of lines of compiler errors when misused. How do you improve this?

Save to ▾
Show answer
Constrain the template so misuse fails early with a clear message. With C++20 I would add a concept or requires clause, for example template <std::totally_ordered T>, so the error names the unmet requirement at the call site. On older standards, a static_assert with a readable message at the top of the function body helps. Good naming and a comment on expected type properties also help.
30

A function returns a string_view and callers sometimes print garbage. What is the likely bug?

Save to ▾
Show answer
The function probably builds or receives a temporary std::string and returns a string_view into it. When the function returns, the string is destroyed and the view dangles. Examples are returning std::string_view(a + b) or a view of a local string. Fix it by returning std::string, or make sure the view refers to data with a longer lifetime, such as a member or static storage.
31

What are type traits and how does if constexpr use them?

Save to ▾
Show answer
Type traits in <type_traits> answer questions about types at compile time, such as std::is_integral_v<T>, std::is_pointer_v<T> or std::is_same_v<T, U>, and transform types, such as std::remove_reference_t. if constexpr picks a branch at compile time and discards the other, so code like if constexpr (std::is_integral_v<T>) can call integer-only operations safely inside a generic function.
32

A generic maxOf template gives wrong results when called with two C-style string literals. Why, and how do you fix it?

Save to ▾
Show answer
T is deduced as const char*, so the template compares pointer addresses instead of the characters. The result depends on where the literals are stored. Fix it by adding an overload for const char* that uses std::strcmp, or better, call it with std::string or std::string_view, where operator< compares contents. This shows why generic code must consider what operations mean for each type.
33

What is the difference between full and partial template specialization?

Save to ▾
Show answer
Full specialization provides a separate implementation for one exact set of template arguments, such as template <> class Hash<std::string>. Partial specialization, allowed only for class and variable templates, covers a family of arguments, such as template <typename T> class Hash<T*> for all pointer types. Function templates cannot be partially specialised; overloading is used instead.
34

Describe a situation where you disagreed with a senior engineer about a design decision.

Save to ▾
Show answer
Situation: a senior wanted a deep inheritance hierarchy for device drivers. Task: I believed composition would be simpler to test. Action: instead of arguing in the meeting, I built a small prototype of both designs, measured test setup effort and shared the results calmly, while accepting that the final call was theirs. Result: we chose a hybrid with a thin interface plus composition, and the senior later asked me to document it for the team.
35

How do you handle a tight deadline when the C++ code you need to change is complex?

Save to ▾
Show answer
Situation: a client needed a protocol change in two days in code I had not touched. Task: deliver safely. Action: I read the tests first, wrote two new tests for the required behaviour, made the smallest change that passed them, and flagged a risky area to my lead early instead of at the end. I logged a refactoring ticket for later. Result: we shipped on time with no regressions and cleaned up the module the next sprint.
36

A generic average function must accept only numeric types. How would you enforce that?

Save to ▾
Show answer
In C++20 I would constrain it: template <typename T> requires std::is_arithmetic_v<T> double average(const std::vector<T>& v). Passing a vector of strings then fails with a clear constraint error. On C++17 the same check can go in a static_assert or std::enable_if. I would also decide whether bool and char should be allowed, since they are technically arithmetic types.
37

Sorting a vector of large objects is slow. What can you do?

Save to ▾
Show answer
Sorting moves elements many times, so large objects that copy instead of move are expensive. First ensure the type has a noexcept move constructor. If moves are still heavy, sort a vector of indices or pointers with a comparator on the key, then reorder once if needed. Precomputing the sort key avoids repeated expensive comparisons. Use std::stable_sort only when equal-element order matters.
38

Tell me about a time you found a difficult bug in your C++ code.

Save to ▾
Show answer
Situation: a service crashed randomly about once a day in production. Task: find the root cause without blocking releases. Action: I enabled core dumps, analysed them in gdb and saw a corrupted vector inside a callback. A staging run with AddressSanitizer showed a use-after-free because a lambda captured a raw pointer to a destroyed object. I changed it to capture a weak_ptr. Result: no crashes for months, and sanitizer runs were added to CI.
39

What are the main components of the Standard Template Library?

Save to ▾
Show answer
The STL has four parts. Containers store data, such as vector, deque, list, map, set and unordered_map. Iterators give a uniform way to traverse containers. Algorithms such as sort, find, accumulate and lower_bound work on iterator ranges. Function objects and lambdas customise algorithms, for example a comparator for sort. Adapters such as stack, queue and priority_queue wrap other containers.
40

How do virtual functions work internally?

Save to ▾
Show answer
Most compilers give each polymorphic class a vtable, a static table of function pointers for its virtual functions. Every object stores a hidden vptr pointing to its class's vtable. A call through a base pointer loads the vptr, looks up the slot and jumps to the right override. The cost is one pointer per object, an indirect call and usually no inlining, which matters only in very hot loops.

Frequently asked questions

What does a C++ Software Engineer do?

A C++ software engineer designs and builds features for large products such as browsers, databases, networking software, CAD tools and cloud infrastructure. They own modules end to end, write modern C++ with smart pointers and templates, review code and improve performance. Interviews combine data structures and algorithms, object-oriented design, modern C++ questions and debugging scenarios. Engineers with two to five years of experience commonly hold this title.

Which skills are tested in a C++ Software Engineer interview?

Expect questions on Modern C++17/20, Templates, Code review, CMake builds, Performance tuning. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a C++ Software Engineer interview?

Revise Modern C++ (C++11 to C++23), Templates and Generic Programming, Standard Template Library (STL), Object-Oriented Programming in C++ first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.