Modern C++ (C++11 to C++23) Interview Questions for C++
25 Modern C++ (C++11 to C++23) questions with answers for c++ roles — 7 easy, 13 medium, 5 hard.
About Modern C++ (C++11 to C++23)
Learn the features that changed how C++ is written: move semantics, auto, lambdas, perfect forwarding, nullptr, optional, variant, string_view, structured bindings, ranges, std::format and std::expected. Understand when each feature helps, its pitfalls such as dangling views, and how to modernise older codebases safely.
Sheet progressStart 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.
auto deduces a variable's type from its initialiser, so auto it = m.begin(); avoids long iterator types. It drops top-level const and references unless you write const auto& or auto&. decltype(expr) yields the declared type of an expression without evaluating it, useful in generic code and return types. decltype(auto) keeps reference-ness exactly, which matters when forwarding return values.
A lambda is an unnamed function object written inline, for example [](int a, int b) { return a < b; }. The capture list decides what outside variables it can use: [x] copies x, [&x] references x, [=] copies everything used, [&] references everything used, and [this] captures the object pointer. C++14 allows init captures such as [p = std::move(ptr)]. Reference captures dangle if the lambda outlives the variables.
NULL is usually defined as 0 or 0L, an integer, so overloaded functions such as f(int) and f(char*) pick the int version for f(NULL), which surprises readers. nullptr has its own type, std::nullptr_t, that converts only to pointer types, so f(nullptr) calls the pointer overload. It also works correctly in templates where the type is deduced. Modern code should always use nullptr.
04
What are structured bindings and how do they work with range-based for loops?
Structured bindings, from C++17, unpack a pair, tuple, array or simple struct into named variables: auto [id, name] = getUser();. In loops they make map iteration readable: for (const auto& [key, value] : m). The variables are aliases into a hidden object, so const auto& avoids copies. They pair well with if statements with initialisers, such as if (auto [it, ok] = s.insert(x); ok).
05
Use lambdas with standard algorithms to sort strings by length and count those longer than a limit.
Move semantics let an object transfer its resources, such as a heap buffer, to another object instead of copying them. A move constructor takes T&& and usually steals the pointer and leaves the source empty but valid. std::move does not move anything; it is just a cast to an rvalue reference that allows a move overload to be chosen. After moving, only assign to or destroy the source.
09
What are std::optional, std::variant and std::any?
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.
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.
11
What is std::string_view and what are its pitfalls?
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.
12
Is it expensive to return a large std::vector from a function by value?
Usually no. Return value optimisation constructs the result directly in the caller's storage, and since C++17 this copy elision is guaranteed when returning a prvalue. For a named local, compilers apply named RVO, and if they cannot, the vector is moved, which only copies three pointers. So return by value is clean and fast. Do not write return std::move(local); because it can block elision.
13
A function returns a string_view and callers sometimes print garbage. What is the likely bug?
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.
14
How would you choose between exceptions, error codes and std::expected for error handling?
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
After calling std::move on a string, a teammate prints it and sees an empty value. Is this a bug?
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.
16
Write a move constructor and move assignment operator for a class that owns a buffer.
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
auto result = v
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; })
| std::views::take(3);
for (int x : result) std::cout << x << ' '; // 4 16 36
}
// Views are lazy: elements are processed only while iterating.
19
Tell me about a time you convinced your team to adopt a new C++ feature or practice.
Situation: our team avoided smart pointers, and leaks kept reaching QA. Task: get agreement to adopt unique_ptr in new code. Action: I prepared a short benchmark showing no measurable overhead, converted one leaky module as a pilot, and presented before and after defect counts at our tech sync. I also wrote clang-tidy rules to help. Result: the team agreed to make it a coding standard and leak bugs dropped sharply in the next quarter.
20
How do you handle a tight deadline when the C++ code you need to change is complex?
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.
Perfect forwarding passes arguments through a function template while preserving whether they were lvalues or rvalues. It uses a forwarding reference, T&& where T is deduced, together with std::forward<T>(arg). Example: template <typename... A> void make(A&&... a) { build(std::forward<A>(a)...); }. Without forward, every named parameter is an lvalue and would be copied. make_unique and emplace_back rely on it.
22
What does noexcept mean and why should move constructors be noexcept?
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.
23
You are asked to modernise a large C++98 codebase to C++17. How would you plan it?
First switch the compiler standard flag and fix build errors, with CI running the full test suite. Then add clang-tidy modernize checks to convert NULL to nullptr, add override and use auto where it helps, applied module by module in small reviewed batches. Next replace raw owning pointers with unique_ptr in new and touched code. I avoid a big-bang rewrite, track progress with metrics and train the team as we go.
24
An asynchronous callback crashes intermittently. It is a lambda that captures local variables by reference. Explain the problem and fix.
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.
25
Tell me about a mistake you made in production and what you learned from it.
Situation: I released a change that used string_view in a cache, and a week later we saw corrupted keys. Task: fix it fast and prevent repeats. Action: I rolled back, reproduced the issue with AddressSanitizer, found the views referenced temporary strings, switched to owning std::string keys and wrote a post-mortem. Result: no further incidents, and I added a lifetime checklist and ASan stage to our pipeline that caught two similar bugs later.
Frequently asked questions
How important is Modern C++ (C++11 to C++23) in C++ interviews?
Learn the features that changed how C++ is written: move semantics, auto, lambdas, perfect forwarding, nullptr, optional, variant, string_view, structured bindings, ranges, std::format and std::expected. Understand when each feature helps, its pitfalls such as dangling views, and how to modernise older codebases safely.
How should I use the Modern C++ (C++11 to C++23) sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.