This C++ skills test guide helps hiring managers and recruiters check C++ engineers for systems, backend, trading, game and embedded roles. A good C++ assessment test tells apart people who know the syntax from people who write safe, fast code with clear ownership. Below is a skills matrix by level, 11 sample questions in modern C++ (C++17 and C++20, noting newer features where they come up), a comparison of test formats and a scoring rubric.
What a C++ skills test should measure
Tell candidates which standard your code uses. Many teams build with C++17 or C++20, and support for C++23 still varies by compiler, so don't test features your toolchain can't build.
Sample C++ test questions
The level and format sit under each question. A 45 to 60 minute test usually covers five or six of them.
1. When do you use unique_ptr, shared_ptr and weak_ptr?
Junior, concept
What good looks like: unique_ptr is the default for single ownership: it is move-only and usually costs nothing over a raw pointer. shared_ptr is for ownership that is truly shared, and it pays for an atomic reference count. weak_ptr observes an object owned by shared_ptr without keeping it alive, which breaks reference cycles such as parent and child links. Create them with std::make_unique and std::make_shared. Raw pointers and references are fine for non-owning access.
2. What goes wrong when you delete a derived object through a base pointer and the base destructor isn't virtual?
Junior, concept
What good looks like: it is undefined behavior. In practice the derived destructor doesn't run, so whatever the derived class owns leaks. A class meant to be deleted through a base pointer needs a virtual destructor. If it isn't meant to be, a protected non-virtual destructor prevents the mistake.
3. A function returns a const std::string& to a local string, and another returns a std::string_view of a temporary. What happens?
Junior to mid, debugging
What good looks like: both dangle. The local is destroyed when the function returns, and the temporary at the end of the full expression, so the reference or view points at freed memory. That is undefined behavior that may pass tests and fail later. Return std::string by value (copy elision and moves make it cheap), or make sure the data outlives the view.
4. Code walks a std::vector with an iterator and calls push_back inside the loop. It crashes only sometimes. Why?
Mid, debugging
What good looks like: if push_back exceeds the capacity, the vector reallocates and every iterator, pointer and reference into it becomes invalid. Even without reallocation, the end iterator is invalidated. "Sometimes" is because it depends on the current capacity. Collect new items separately, or loop by index with care. For removal, use std::erase_if (C++20) or the erase-remove idiom.
5. What does std::move actually do?
Mid, concept
What good looks like: nothing at runtime. It is a cast to an rvalue reference that lets the move constructor or move assignment be chosen. Standard library objects are left valid but in an unspecified state after a move, so you can assign a new value or destroy them, but shouldn't rely on their contents. Strong candidates add that std::move on a const object silently copies, and that return std::move(local); can block copy elision.
6. What is the rule of zero, and when do you need the rule of five?
Mid, concept
What good looks like: rule of zero: let members that manage resources (smart pointers, containers) handle copying, moving and destruction, so your class declares none of the five special members. Rule of five: if you write a custom destructor, copy constructor, copy assignment, move constructor or move assignment, you probably need all five. For example, declaring a destructor stops the compiler from generating move operations, so moves quietly become copies.
7. Wrap a C library FILE* so it is always closed, even on early returns and exceptions.
Mid, practical
What good looks like: use RAII. A tidy version is a deleter struct, struct FileCloser { void operator()(FILE* f) const { std::fclose(f); } };, then std::unique_ptr<FILE, FileCloser> file(std::fopen(path, "r"));. Check for null before use. unique_ptr won't call the deleter on a null pointer, so a failed open is safe.
8. Two threads each increment a shared int a million times, and the total comes out short. Why, and how do you fix it?
Mid to senior, debugging
What good looks like: ++counter is a read, an add and a write, and unsynchronized concurrent access is a data race, which is undefined behavior in C++. Use std::atomic<int> with fetch_add (relaxed ordering is enough for a pure counter), or a std::mutex with std::scoped_lock when several values must change together. ThreadSanitizer finds this kind of bug.
9. Why should a move constructor be marked noexcept?
Senior, concept
What good looks like: when a std::vector grows, it moves existing elements only if the move constructor is noexcept (it uses std::move_if_noexcept). Otherwise it copies them to keep the strong exception guarantee. A missing noexcept can silently turn every reallocation into a full copy. Move-only types are moved regardless.
10. Write a function template that accepts only integer types, using C++20.
Senior, practical
What good looks like: template <std::integral T> T gcd(T a, T b), or the same with a requires clause, after including <concepts>. Concepts give a clear error at the call site instead of pages of template errors, and replace most std::enable_if tricks. A strong candidate also points out that std::gcd already exists in <numeric>.
11. After a compiler upgrade, a release build gives wrong results while debug builds look fine. Where do you start?
Senior, practical scenario
What good looks like: suspect undefined behavior that the optimizer now exploits: signed integer overflow, uninitialized variables, out-of-bounds access, strict aliasing violations or a data race. Build with -fsanitize=address,undefined (ThreadSanitizer in a separate build), turn on -Wall -Wextra, and narrow down the code with a bisect. Lowering the optimization level hides the bug rather than fixing it.
Test formats compared
For many C++ roles, a strong first screen is a conversation about ownership, lifetimes and debugging, followed by a work sample built on your own toolchain. See our technical screen definition for how the first step fits the loop.
Scoring rubric
Running a C++ test with NTRVSTA
C++ is one of the programming languages in NTRVSTA's exam library. The live AI interviewer adjusts each question to the last answer, so a candidate who handles move semantics well moves on to exception guarantees and concurrency, while someone who struggles with ownership gets a more basic question or a hint. It also asks follow-ups on reasoning, which matters in C++, where a right-sounding answer can hide undefined behavior. Senior candidates can take a system-design question on the interactive whiteboard.
Reviewers get a score breakdown (technical skills, English proficiency, communication), video snippets of the key moments and a shareable report. For broader rounds, use our software engineer interview questions and system design interview questions. To compare dedicated code-execution platforms, see the best coding assessment tools, and for neighboring stacks see the Java skills test.
Questions
How long should a C++ assessment test be?
Plan 45 to 60 minutes for a screen. C++ answers often need more explanation than other languages, so cover fewer questions in more depth rather than rushing through many.
Which C++ standard should the test use?
Use the standard your code builds with, usually C++17 or C++20, and say so in the instructions. Treat C++23 features as a bonus, since compiler support still varies.
Should a C++ test cover embedded or low-level topics?
Only for roles that need them. Embedded and trading roles may need questions on memory layout, allocation and latency, while an application team gets more signal from ownership, the standard library and debugging.
Thinking about adaptive screening for your C++ roles? Book a 20-minute demo, or take a sample interview.




