Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.
About Goldman Sachs hiring for C++ roles
Goldman Sachs is a global investment banking, securities and asset management firm. Its Bengaluru and Hyderabad offices are major engineering and operations centres supporting trading, risk, compliance and consumer platforms. It hires in India for software engineers, quality engineers, data engineers, quantitative strategists and risk or operations analysts.
For C++ roles, this Goldman Sachs practice set focuses most on Modern C++ (C++11 to C++23) (6), Templates and Generic Programming (5), Multithreading and Concurrency (5), C++ Basics and Syntax (5). It has 4 easy, 24 medium, 2 hard questions, so you can start easy and work up to the harder ones.
Company type
Banking & finance
Headquarters
New York, USA
Main India offices
Bengaluru, Hyderabad, Mumbai
Goldman Sachs C++ interview process
Online assessment (coding + quant + aptitude)
Technical round 1 (DSA)
Technical round 2 (design or CS fundamentals)
Hiring manager round
HR or culture round
Typical pattern based on public candidate experiences. Rounds vary by role, team and year.
Solved for Goldman SachsStart 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 are type traits and how does if constexpr use them?
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.
02
Write a move constructor and move assignment operator for a class that owns a buffer.
Input and output iterators support a single pass, such as stream iterators. Forward iterators allow multiple passes, as in forward_list. Bidirectional iterators also move backwards, as in list, map and set. Random access iterators support jumps and arithmetic, as in vector and deque, and C++20 adds contiguous iterators. Algorithms require a minimum category, so std::sort needs random access and cannot sort a std::list.
04
Sorting a vector of large objects is slow. What can you do?
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.
05
A program crashes with terminate called without an active exception when a function returns. What is the cause?
A std::thread object that is still joinable was destroyed, often because the function returned early or threw before reaching join. The std::thread destructor calls std::terminate in that case. Fix it by making sure every thread is joined on all paths, using an RAII guard, or switching to std::jthread, which joins automatically. Be careful with detach, since detached threads may access destroyed data.
06
What is the erase-remove idiom and what replaced it in C++20?
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.
07
What are AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer?
They are compiler instrumentation tools in GCC and Clang. ASan, -fsanitize=address, detects out-of-bounds access, use-after-free, double free and leaks with about 2x slowdown. UBSan, -fsanitize=undefined, catches signed overflow, invalid shifts and null dereferences. TSan, -fsanitize=thread, detects data races but cannot be combined with ASan. Running tests under sanitizers in CI catches bugs long before production.
constexpr unsigned long long factorial(unsigned n) {
unsigned long long r = 1;
for (unsigned i = 2; i <= n; ++i) r *= i;
return r;
}
static_assert(factorial(5) == 120);
constexpr auto f10 = factorial(10); // computed by the compiler
// Pre-C++14 style used template recursion:
// template <unsigned N> struct Fact { static const unsigned long long v = N * Fact<N - 1>::v; };
// template <> struct Fact<0> { static const unsigned long long v = 1; };
09
A teammate complains that a template function produces hundreds of lines of compiler errors when misused. How do you improve this?
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.
10
Code that takes bool& from a std::vector<bool> fails to compile. Why?
std::vector<bool> is a special case that packs bits, so elements are not real bool objects and operator[] returns a proxy object, not bool&. You cannot take a reference or pointer to a single element, and it is not thread-safe to write neighbouring elements from different threads. If you need real bools use std::vector<char>, std::deque<bool>, or std::bitset when the size is fixed.
11
Compare pass by value, pass by pointer and pass by reference.
Pass by value copies the argument, so changes inside the function do not affect the caller. Pass by pointer passes an address, can be null and needs dereferencing. Pass by reference creates an alias that cannot be null and uses normal syntax. A good rule: pass small types such as int by value, large read-only objects by const reference, and use a non-const reference or pointer for output parameters.
12
How does std::condition_variable work and what are spurious wakeups?
A condition variable lets a thread sleep until another thread signals that some state changed. The waiter holds a unique_lock and calls cv.wait(lock, predicate), which releases the mutex while sleeping and reacquires it on wake. The notifier changes the state under the mutex and calls notify_one or notify_all. Threads can wake without notification, called spurious wakeups, so always wait with a predicate that rechecks the condition.
13
Why should you prefer make_unique and make_shared over calling new directly?
They avoid writing raw new, so no allocated object can leak if another expression in the same call throws. make_shared also allocates the object and the control block together in one allocation, which is faster and more cache friendly. One trade-off: with make_shared the memory block stays allocated until every weak_ptr is gone, which matters for very large objects.
14
What is the difference between a process and a thread?
A process is an independent program with its own address space, file handles and resources; processes are isolated and communicate through IPC such as pipes, sockets or shared memory. Threads live inside a process and share its memory and handles, but each has its own stack and registers. Threads are cheaper to create and communicate faster, but shared memory needs synchronisation to avoid races.
15
Two third-party libraries both define a global function called init and your build fails with duplicate symbols. How do you fix it?
The clean fix is to place each library's code in its own namespace so calls become liba::init and libb::init. If the source cannot change, I would check whether one function is only used internally and give it internal linkage, or wrap one library behind a shared library that hides its symbols using visibility settings. I avoid macro renaming hacks because they break silently on upgrades.
16
Write a C++ program that counts how often each word appears in the input.
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, int> freq;
std::string word;
while (std::cin >> word) {
++freq[word];
}
for (const auto& [w, c] : freq) {
std::cout << w << ' ' << c << '\n';
}
}
// Use std::unordered_map for O(1) average lookups if sorted output is not needed.
17
What does C++20 std::jthread add over std::thread?
std::jthread joins automatically in its destructor, so forgetting join no longer calls std::terminate. It also supports cooperative cancellation: the thread function can take a std::stop_token, check stop_requested() in its loop, and the owner calls request_stop(), which the destructor also does before joining. std::stop_callback lets you register cleanup actions. This makes long-running worker threads safer to write.
18
What is the difference between full and partial template specialization?
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.
19
Design a producer-consumer pipeline where one thread reads log lines and several workers parse them.
I would use a bounded thread-safe queue built with a mutex and two condition variables, one for not empty and one for not full, so a fast reader cannot exhaust memory. The reader pushes lines and workers pop and parse them. For shutdown, the reader pushes a sentinel per worker or sets a closed flag and notifies all. Batching lines per push reduces locking overhead, and results go to a second queue.
20
Use a C++20 ranges pipeline to get squares of the first three even numbers.
#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.
unique_ptr has sole ownership, cannot be copied, can only be moved, and costs almost nothing over a raw pointer. shared_ptr keeps a reference count in a control block and destroys the object when the last owner disappears. weak_ptr observes an object owned by shared_ptr without increasing the count; call lock() to get a shared_ptr if it still exists. weak_ptr breaks cycles and suits caches.
22
Use std::variant and std::visit to handle different message types.
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.
Inside a function, a static local variable is initialised once and keeps its value between calls. At namespace scope, static gives internal linkage, so the name is visible only in that translation unit. Inside a class, a static data member belongs to the class rather than each object, and a static member function has no this pointer. Since C++17 inline static members can be defined directly in the header.
26
What is function overloading and how does the compiler choose an overload?
Overloading means several functions share a name but differ in the number or types of parameters; return type alone is not enough. The compiler runs overload resolution: exact matches win, then promotions such as char to int, then standard conversions, then user-defined conversions. If two candidates are equally good the call is ambiguous and compilation fails. Name mangling gives every overload a unique linker symbol.
27
When do you need the typename keyword inside a template?
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.
28
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.
A dangling pointer points to memory that has already been freed or has gone out of scope, for example the address of a local variable returned from a function, or a pointer used after delete. Dereferencing it is undefined behaviour and may crash much later in unrelated code. Prevent it with smart pointers, clear ownership rules and by never returning addresses of local variables.
30
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.
Tips to crack the Goldman Sachs C++ interview
Practise probability and quantitative aptitude questions, which commonly appear in Goldman Sachs online assessments.
Learn basic financial concepts such as equities, bonds, derivatives and risk, especially for strats and data roles.
Prepare clear stories about teamwork and integrity, as Goldman Sachs emphasises its business principles and client focus.
Keep a one-minute story ready for every project on your CV: problem, your part, tools, result.
Practise the hard questions on this page aloud; Goldman Sachs interviewers usually ask follow-ups on your first answer.
Frequently asked questions
How many rounds are there in the Goldman Sachs C++ interview?
Candidates usually go through 5 stages: Online assessment (coding + quant + aptitude); Technical round 1 (DSA); Technical round 2 (design or CS fundamentals); Hiring manager round; HR or culture round. The exact rounds change with the role, team, location and your experience, so confirm the process with the recruiter.
Which topics should I prepare for the Goldman Sachs C++ interview?
This practice set leans on Modern C++ (C++11 to C++23) (6), Templates and Generic Programming (5), Multithreading and Concurrency (5), C++ Basics and Syntax (5). Revise those first, then practise explaining one or two of your own projects end to end, because most Goldman Sachs interviewers ask you to walk through real work.
Are these the exact questions asked at Goldman Sachs?
No. These are curated practice questions that match the skills and difficulty usually tested in banking & finance interviews like Goldman Sachs's. They are not a leaked or verified list of past questions.
Where does Goldman Sachs hire C++ professionals in India?
Goldman Sachs has large teams in Bengaluru, Hyderabad, Mumbai. Openings and locations change often, so check the Goldman Sachs careers page and filter by city and experience.