Functions
Declarations, overloading, default arguments, inline, lambdas, and callable design.
37 questions
JuniorTheoryVery commonPassing arguments by value, reference, and pointer — when to use each?
Passing arguments by value, reference, and pointer — when to use each?
By value: cheap-to-copy types (two or three machine words) or when you need a copy. By const T&: read-only types costlier to copy. By T&: to modify the caller's object. By T*: when null is meaningful.
Common mistakes
- ✗Passing large objects by value in performance-sensitive code — causes an unnecessary copy
- ✗Using a non-const reference when only reading the value — misleads callers into thinking the object may be modified
- ✗Dereferencing a pointer parameter without a null check when the pointer may legally be null
Follow-up questions
- →What is the 'sink parameter' pattern and when does passing by value +
std::moveoutperform overloading lvalue/rvalue refs? - →How does passing by
T&&(rvalue reference) differ semantically from passing byT?
JuniorDebuggingVery commonWhat happens if you return a reference to a local object?
What happens if you return a reference to a local object?
Undefined behavior: count dies at return, so c refers to a dead variable
Common mistakes
- ✗Expecting the reference to keep
countalive: nothing extends a local's lifetime pastreturn, so readingcis undefined behavior. - ✗Expecting a compile error: GCC and Clang warn by default, even without
-Wall, but the program still builds.
Follow-up questions
- →How does copy elision (RVO/NRVO) make returning a large object by value cheap?
- →Is returning a reference to a
staticlocal variable safe in a multithreaded program?
JuniorTheoryVery commonWhat is a lambda? How do you capture outer variables?
What is a lambda? How do you capture outer variables?
Anonymous function object created inline. Captures: [=] value, [&] ref, [x]/[&x], [y = expr] init, [*this] copy. Captureless converts to a function pointer.
Common mistakes
- ✗Using
[&]capture in a lambda stored beyond the captured variables' lifetimes — dangling references - ✗Forgetting that
[=]capturesthisby pointer in a member function — use[*this](C++17) for a safe copy - ✗Expecting two identical lambda expressions to have the same type — each lambda has its own unique type
Follow-up questions
- →What is a generic lambda (C++14) and how does it relate to function templates?
- →When would you use a
mutablelambda?
JuniorTheoryVery commonWhat is function overloading and how does overload resolution work?
What is function overloading and how does overload resolution work?
Multiple functions share a name but differ in parameter types or count. The compiler picks the best match: exact type, promotions, standard conversions, user-defined conversions.
Common mistakes
- ✗Trying to overload on return type only — not allowed in C++
- ✗Creating ambiguous overloads that force the compiler to emit an error
- ✗Forgetting that default arguments can create ambiguity with existing overloads
Follow-up questions
- →How does name mangling relate to overloading?
- →What happens when no overload matches exactly?
MiddleDebuggingVery commonWhy does this returned lambda read freed memory?
Why does this returned lambda read freed memory?
The lambda captures count by reference ([&]), but count is destroyed when makeCounter returns, so calling the returned std::function reads a dangling reference — UB. Fix: capture by value, [count]() mutable { return ++count; }.
Common mistakes
- ✗Believing reference capture extends the captured variable's lifetime
- ✗Blaming std::function instead of the by-reference capture
- ✗Thinking adding mutable fixes what is actually a lifetime bug
Follow-up questions
- →When is capturing by reference in a lambda actually safe?
- →Why does a by-value capture need
mutableto be modified?
JuniorTheoryCommonWhat are the rules for default arguments and what pitfalls do they introduce?
What are the rules for default arguments and what pitfalls do they introduce?
Default arguments must be listed right-to-left (trailing first). Resolved at the call site using the declaration visible there, not the definition.
Common mistakes
- ✗Giving one function different default values in different TUs — since C++20 an ODR violation, ill-formed with no diagnostic required; GCC and Clang, even with LTO, accept it silently
- ✗Combining default arguments with overloads to create ambiguous calls
- ✗Expecting virtual overrides to inherit default arguments from the base
Follow-up questions
- →Why do virtual functions and default arguments interact poorly?
- →What happens if two translation units see different default arguments for the same function?
JuniorTheoryCommonWhat is the order of evaluation of function arguments in C++?
What is the order of evaluation of function arguments in C++?
The order is unspecified and differs between compilers. Since C++17 each argument is evaluated completely before another starts, so f(a++, a++) — UB before C++17 — just passes a and a+1 in some order.
Common mistakes
- ✗Assuming left-to-right evaluation because one compiler does it — GCC and Clang already disagree on x86-64
- ✗Relying on what
f(i++, i)passes — not UB since C++17, but whetheriis read before or after the increment is unspecified - ✗Thinking C++17 fixed the argument order — it only sequenced the callee first and stopped arguments from interleaving
Follow-up questions
- →What sequencing rules did C++17 introduce that C++14 did not have?
- →How do you rewrite
f(i++, i++)so the values passed no longer depend on the compiler?
JuniorTheoryCommonWhat does the program print?
What does the program print?
1 5
Open full question →Common mistakes
- ✗Expecting
5 5:[x]copiesxwhen the lambda is created, so later changes toxdo not reach it. - ✗Expecting
1 1:[x]copiesxinto the lambda, butxitself stays an ordinary variable, sox = 5changes it.
Follow-up questions
- →What does the program print if the capture is
[&x]instead of[x]? - →Why does
[x]() { ++x; }not compile withoutmutable?
JuniorTheoryCommonWhat exit code does main return in this program?
What exit code does main return in this program?
0
Open full question →Common mistakes
- ✗Expecting a random value: flowing off the end of any other value-returning function is undefined behavior, but
mainalone returns0. - ✗Expecting a compile error: of all functions that return a value, only
mainmay end withoutreturn.
Follow-up questions
- →What happens when another function returning
intreaches its closing brace withoutreturn? - →What do the
<cstdlib>macrosEXIT_SUCCESSandEXIT_FAILUREmean as exit codes?
JuniorTheoryCommonWhat does the program print?
What does the program print?
double
Open full question →Common mistakes
- ✗Expecting
int:float→intis a conversion,float→doublea promotion, and a promotion beats a conversion. - ✗Expecting an ambiguity error: a call is ambiguous only when the best overloads are equally good, and only
doubleis reached by a promotion.
Follow-up questions
- →What would
print(2.5f)call if onlyprint(int)andprint(long)were declared? - →Why does
print('a')callprint(int)here rather thanprint(long)?
JuniorTheoryCommonWhat does the program print?
What does the program print?
1 10
Open full question →Common mistakes
- ✗Expecting
10 10:xis a copy ofn, so assigning toxleavesnunchanged. - ✗Expecting
1 1: a function may change its own copy, andreturn xreturns the changed value.
Follow-up questions
- →How would you change
resetso that the call also setsnto10? - →When is passing a parameter by
constreference better than passing it by value?
JuniorTheoryCommonWhat is a function pointer and how do you declare one?
What is a function pointer and how do you declare one?
Holds the address of a function with a specific signature. Declaration: int (*fp)(double, double);. Call: fp(a, b). Use using BinaryOp = int(*)(double, double); for readability.
Common mistakes
- ✗Confusing pointer-to-function with pointer-to-member-function — member function pointers have a different, incompatible type
- ✗Not using a
typedef/usingfor the function pointer type — raw declaration syntax is notoriously hard to read - ✗Storing a capturing lambda in a raw function pointer — only non-capturing lambdas are implicitly convertible
Follow-up questions
- →What is the difference between
std::functionand a raw function pointer in terms of overhead? - →How do you store a pointer to a non-static member function and call it later?
JuniorTheoryCommonWhat is short-circuit (lazy) evaluation in C++?
What is short-circuit (lazy) evaluation in C++?
&& and || evaluate the left operand first; if the result is determined, the right is skipped. Standard-guaranteed and sequenced. The ternary ? : also short-circuits. Overloaded &&/|| do NOT.
Common mistakes
- ✗Relying on short-circuit evaluation with overloaded
&&/||— overloaded operators always evaluate both sides - ✗Placing a side-effect-heavy expression on the right, expecting it to always run — it won't if the left determines the result
- ✗Not knowing that the comma operator also sequences left-before-right, but
|and&do not short-circuit
Follow-up questions
- →How do
std::logical_andandstd::logical_ordiffer from&&and||? - →When is short-circuit evaluation useful for guarding null pointer checks?
JuniorTheoryCommonWhat is a function stack frame and what can cause stack overflow?
What is a function stack frame and what can cause stack overflow?
Per-call area for local variables, saved registers, return address, spilled arguments. Stack overflow happens when nested calls or large locals exceed the stack limit.
Common mistakes
- ✗Assuming the stack grows without practical limits
- ✗Allocating multi-megabyte arrays as local variables
- ✗Confusing the call stack with the C++ container std::stack
Follow-up questions
- →Where are function instructions stored?
- →How would you avoid stack overflow in recursive code?
MiddleTheoryCommonWhat is argument-dependent lookup (ADL), and when does it surprise you?
What is argument-dependent lookup (ADL), and when does it surprise you?
ADL adds the namespaces of a call's argument types to the set searched for an unqualified function name. It lets swap(a, b) or operator<< find the right overload without a qualifier. It surprises you by silently pulling in an unintended overload from an argument's namespace.
Common mistakes
- ✗Writing
std::swap(a, b)instead of theusing std::swap; swap(a, b)idiom that letsADLfind a customswap - ✗Assuming a qualified call
ns::f(x)still doesADL— qualification disables it entirely - ✗Defining a free function in
namespace stdto makeADLfind it, instead of placing it next to the type
Follow-up questions
- →Why is the
using std::swap; swap(a, b)two-step idiom recommended? - →How do hidden friends interact with ADL and ordinary lookup?
MiddleTheoryCommonHow does C++14 return-type deduction work and when does it fail?
How does C++14 return-type deduction work and when does it fail?
auto f() { return expr; } deduces the return type by the same rules as auto x = expr. All returns must yield the same type. Recursion only after the first return.
Common mistakes
- ✗Two
returnstatements yielding different types — compile error - ✗Recursive call before any
return— compiler can't deduce yet - ✗Forward-declaring
auto f();and defining elsewhere — caller can't see deduced type
Follow-up questions
- →Why does
decltype(auto)matter for forwarding return types perfectly? - →How does the lambda's
autoreturn differ from a function's?
MiddleTheoryCommonWhat does constexpr on a function mean and how has it evolved through C++14/17/20?
What does constexpr on a function mean and how has it evolved through C++14/17/20?
Permits compile-time evaluation with constant args; otherwise runs at runtime. C++11 — single return. C++14 — loops, locals. C++20 — virtual, allocations, std::vector/std::string.
Common mistakes
- ✗Marking everything
constexprregardless of need — no harm but adds API surface - ✗Calling a non-constexpr function (e.g. legacy library) inside a constexpr function — silently turns it into a runtime call
- ✗Confusing
constexprwithconst—constexprimplies const for objects, but for functions it's about evaluatability
Follow-up questions
- →What's the difference between
constexprandconsteval? - →What is
std::is_constant_evaluated()and when do you use it?
MiddleDebuggingCommonWhat are the pitfalls of default function arguments in virtual functions and across translation units?
What are the pitfalls of default function arguments in virtual functions and across translation units?
Defaults bind statically by the static type of the pointer/reference. Base* b = new Derived; b->f(); uses Base's default though Derived::f runs — the override's = 2 is dead.
Common mistakes
- ✗Overriding a virtual with a different default value — call uses base's default
- ✗Putting different defaults in two headers — ODR violation
- ✗Defaults that depend on globals — non-obvious behaviour at call site
Follow-up questions
- →Why does the standard pick static binding for default arguments?
- →How does
std::optional/ sentinel value replace default arguments cleanly?
MiddleTheoryCommonWhat is a functor? Write an example.
What is a functor? Write an example.
A functor (function object) is any class that overloads operator(). Unlike a function pointer, it carries state in members and inlines — no type-erasure overhead.
Common mistakes
- ✗Forgetting to mark
operator()asconstwhen the functor does not mutate state — prevents use withconstalgorithms - ✗Storing expensive-to-copy state in a functor passed by value to an algorithm — STL may copy the functor internally
- ✗Using
std::functionwhen a template parameter accepts the callable —std::functionadds type-erasure overhead unnecessarily
Follow-up questions
- →What is the difference between a stateful functor and a lambda with captures?
- →How does
std::bindrelate to functors and why is it mostly superseded by lambdas?
MiddleTheoryCommonWhat does the inline keyword actually guarantee in modern C++?
What does the inline keyword actually guarantee in modern C++?
inline suppresses ODR so the same definition can appear in multiple TUs — useful for header functions. The expansion hint is mostly ignored by modern compilers.
Common mistakes
- ✗Assuming inline always eliminates the call overhead
- ✗Thinking inline gives the function internal linkage
- ✗Placing non-inline definitions in headers and expecting no linker errors
Follow-up questions
- →What is the difference between inline and __forceinline / __attribute__((always_inline))?
- →When would you use NOINLINE?
MiddleTheoryCommonWhat does mutable mean for lambdas and what are the risks of capturing this?
What does mutable mean for lambdas and what are the risks of capturing this?
By default a lambda's operator() is const, so value captures can't be modified. mutable removes that const. Capturing this stores a pointer — dangles if the object dies first.
Common mistakes
- ✗Thinking mutable changes the original captured-by-value variable
- ✗Capturing this into async callbacks without controlling object lifetime
- ✗Using [&] in callbacks that outlive the current scope
Follow-up questions
- →How can shared_ptr or weak_ptr help with async callbacks?
- →What is the difference between [this] and [*this]?
MiddleTheoryCommonWhat is C++ name mangling and how does it interact with extern "C"?
What is C++ name mangling and how does it interact with extern "C"?
Mangling encodes signatures (parameters, namespaces, templates) into the symbol name so the linker distinguishes overloads. Each ABI has its own scheme. extern "C" disables mangling for C interop.
Common mistakes
- ✗Forgetting
extern "C"on a function called from C — link error (undefined reference) - ✗Trying to overload an
extern "C"function — there's no mangling, so you can't - ✗Mismatch between ABI versions when linking object files compiled with different compilers
Follow-up questions
- →How do you read a mangled name (
c++filt)? - →Why is the Itanium ABI used on Linux despite the name?
MiddlePerformanceCommonWhat is the runtime cost of a virtual function call and when does it matter?
What is the runtime cost of a virtual function call and when does it matter?
Two indirections: load vptr, load function pointer from vtable, jump. Cost is a few ns plus a possible cache miss; more importantly the call cannot be inlined.
Common mistakes
- ✗Profiling without
-O2and concluding virtual is too slow - ✗Adding
virtualto leaf-only methods that could befinal(loses devirt opportunity) - ✗Choosing CRTP for shallow polymorphism with one or two types — overkill
Follow-up questions
- →How does the compiler perform devirtualisation?
- →Why does
finalhelp even on a class (not just on a method)?
SeniorTheoryCommonWhat is perfect forwarding and how do you implement it?
What is perfect forwarding and how do you implement it?
Preserves value category (lvalue/rvalue) and cv-qualification when forwarding args. Pattern: template<class... A> void wrap(A&&... a) { f(std::forward<A>(a)...); }. Without forward, rvalues become lvalues.
Common mistakes
- ✗Using
std::move(args)...instead ofstd::forward<Args>(args)...— wrongly moves lvalues - ✗Confusing
T&&in non-template context with a forwarding reference — it's just rvalue ref there - ✗Overloading on forwarding refs vs concrete types — forwarding ref usually wins, hijacking calls
Follow-up questions
- →What is reference collapsing?
- →How does C++20
std::forward_likediffer fromstd::forward?
JuniorTheoryOccasionalWhat does the program print?
What does the program print?
A
Open full question →Common mistakes
- ✗Expecting
AB:returnendsmainat once, so the statement after it never runs. - ✗Expecting a compile error: code after
returncompiles, but it never runs.
Follow-up questions
- →Why does the compiler accept the line printing
B, even though it can never run? - →Is output sent to
std::coutwithout a trailing newline still printed whenmainreturns?
MiddleTheoryOccasionalWhat is a function contract (preconditions, postconditions, invariants)?
What is a function contract (preconditions, postconditions, invariants)?
Obligations between caller and callee. Preconditions — caller must satisfy. Postconditions — callee guarantees. Invariants — properties holding after every public op.
Common mistakes
- ✗Silently handling violated preconditions instead of asserting — hides caller bugs and leads to harder-to-debug failures downstream
- ✗Not documenting the contract — callers cannot know what is valid without it
- ✗Checking preconditions in release builds when they are expensive — use
assertwhich compiles out in NDEBUG
Follow-up questions
- →How do
assert,[[expects]], and exception throwing differ as precondition enforcement mechanisms? - →What is Design by Contract (DbC) and which languages support it natively?
MiddleTheoryOccasionalWhat is a generic lambda, and how does it relate to a function template?
What is a generic lambda, and how does it relate to a function template?
A generic lambda (C++14) uses auto for a parameter, making its operator() a member template. Each distinct argument type instantiates a separate operator(), exactly like a function template. The closure is one type; only the call operator is templated and adapts per call site.
Common mistakes
- ✗Thinking each
auto-parameter call produces a new closure type — there is one closure type with a templatedoperator() - ✗Expecting two argument types to share one
operator()body — each type instantiates its own, with its own errors - ✗Forgetting
decltype(auto)orauto&&for parameters that must perfectly forward inside the body
Follow-up questions
- →How do you add an explicit template parameter list to a lambda (C++20)?
- →When would a generic lambda be preferable to a named function template?
MiddleTheoryOccasionalWhat is an init-capture, and why is it needed to capture a variable by move?
What is an init-capture, and why is it needed to capture a variable by move?
An init-capture (C++14) introduces a new closure member with an initializer: [p = std::move(ptr)]. Plain [ptr] only copies; the capture syntax cannot express a move. Init-capture lets you initialize the member from any expression — moving a unique_ptr in, or computing a fresh value.
Common mistakes
- ✗Trying to capture a
unique_ptrwith plain[ptr]and hitting a deleted-copy-constructor error - ✗Forgetting that the init-capture member is initialized once, when the lambda is created, not per call
- ✗Naming the init-capture member the same as the source variable and confusing which one the body uses
Follow-up questions
- →How do you move a member into a lambda inside a member function?
- →Can an init-capture use a pack expansion in C++20?
MiddleCodeOccasionalImplement a minimal signal/slot mechanism using lambdas and std::function
Implement a minimal signal/slot mechanism using lambdas and std::function
Store callbacks as std::vector<std::function<void()>> keyed by token, and iterate on emit(). Lambdas with captures fit naturally — std::function type-erases the closure.
Common mistakes
- ✗Capturing local variables by reference when the lambda outlives the scope
- ✗Copying
std::functionobjects that wrap large captures — preferstd::move - ✗Forgetting that
std::functionhas ~10-20% overhead vs a direct call due to heap allocation and type erasure
Follow-up questions
- →How would you make the slot connection thread-safe?
- →What is
std::move_only_functionand when would you choose it overstd::function?
MiddleTheoryOccasionalWhen do you need std::mem_fn and when has it been replaced by lambdas?
When do you need std::mem_fn and when has it been replaced by lambdas?
std::mem_fn(&Class::method) adapts a member-function pointer into a callable taking the object first. Useful with STL algorithms; modern code prefers a lambda.
Common mistakes
- ✗Using
std::bindfor what a lambda expresses better — bind composition is hard to read - ✗Forgetting that
mem_fncan take pointer or reference and dispatches accordingly - ✗Storing
mem_fn's result in a typed variable — auto is the practical choice
Follow-up questions
- →Why is
std::bindlargely deprecated by lambdas? - →How would you bind only some arguments while leaving others free?
MiddleTheoryOccasionalWhat is a trailing return type and when is it required?
What is a trailing return type and when is it required?
auto f(args) -> ReturnType puts the return type after the parameter list. Required when the return type depends on params: auto add(T a, U b) -> decltype(a + b). Still needed for SFINAE-friendly templates.
Common mistakes
- ✗Using just
autofor templates participating in overload resolution — caller can't SFINAE on the return type - ✗Forgetting that
auto f() -> intis identical toint f()for non-templates - ✗Mixing
decltype(expr)with reference-collapsing surprises in trailing return
Follow-up questions
- →How does
decltype(auto)differ fromautoin return type? - →When does C++14
autodeduction fail and require-> T?
MiddleTheoryOccasionalHow do C-style variadic functions differ from C++11 variadic templates?
How do C-style variadic functions differ from C++11 variadic templates?
C-style ... with va_list is type-unsafe — callee guesses types via format string or sentinel; mismatches cause UB. C++11 variadic templates template<class... A> are type-safe — each arg keeps its type.
Common mistakes
- ✗Mixing
va_listand a variadic template in the same API - ✗Forgetting fold expressions:
(std::cout << ... << args)is much cleaner than recursive base+general overloads - ✗Passing non-trivially-copyable types through
va_list— UB
Follow-up questions
- →What are unary vs binary fold expressions?
- →How does
std::format(C++20) replace printf-style varargs safely?
SeniorTheoryOccasionalHow does noexcept(expr) enable conditional noexcept on templates?
How does noexcept(expr) enable conditional noexcept on templates?
noexcept(expr) evaluates expr at compile time and propagates the bool into the noexcept spec. Pattern: swap(T&, T&) noexcept(noexcept(swap(a.m, b.m))).
Common mistakes
- ✗Writing
noexcept(true)on a function that calls something potentially throwing — terminate at runtime - ✗Forgetting the inner
noexcept(...)and writingnoexcept(swap(a, b))(which is the operand!) — common typo - ✗Marking move ctor
noexcepteven when a member's move can throw — UB by terminate
Follow-up questions
- →Why does the standard library require
noexceptmove for the strong guarantee in vector? - →How does
is_nothrow_move_constructible_vget evaluated?
SeniorPerformanceOccasionalWhen does the compiler perform tail-call optimisation in C++?
When does the compiler perform tail-call optimisation in C++?
The C++ standard does not require TCO. GCC/Clang/MSVC do tail-call optimisation when the recursive call is the last op at -O2/-O3, with no destructors after and compatible ABI.
Common mistakes
- ✗Returning
std::move(recursive_call())— disables TCO because of the wrapper - ✗Having a destructor run after the recursive call (e.g. local with non-trivial dtor)
- ✗Compiling debug and being surprised TCO doesn't fire
Follow-up questions
- →Why doesn't C++ standardise TCO?
- →How does
[[clang::musttail]]change the picture?
SeniorTheoryOccasionalWhat is the INVOKE operation, and what does std::invoke unify?
What is the INVOKE operation, and what does std::invoke unify?
INVOKE is the standard's abstract rule for calling any callable. std::invoke(f, args...) applies it uniformly: it calls free functions and functors as f(args...), but for a pointer-to-member it treats the first argument as the object, handling references, pointers, and reference_wrapper. It underpins std::bind, std::thread, and std::function.
Common mistakes
- ✗Hand-writing branchy code to call either a free function or a pointer-to-member when
std::invokealready unifies both - ✗Assuming
std::invokeadds runtime cost — it is fully resolved at compile time and inlines away - ✗Forgetting
std::invoke_result_t/std::is_invocable_vexist to query the same INVOKE rule in traits
Follow-up questions
- →How does
std::invoke_r(C++23) differ fromstd::invoke? - →Why do
std::bind,std::thread, andstd::asyncall specify their call in terms of INVOKE?
SeniorTheoryRareWhat is consteval and when should you use it instead of constexpr?
What is consteval and when should you use it instead of constexpr?
consteval (C++20) marks a function as immediate — every call must yield a constant expression. Unlike constexpr, no runtime fallback. Used for compile-time-only utilities.
Common mistakes
- ✗Marking a function
constevaland trying to call it with runtime arguments - ✗Taking a pointer to a
constevalfunction — error - ✗Confusing
constevalwithconstinit(which initialises a variable at compile time)
Follow-up questions
- →How does
constevalinteract withif consteval(C++23)? - →Why was
constevaladded ifconstexpralready supported compile-time evaluation?
SeniorTheoryRareWhat is C++23 "deducing this" and what does it solve?
What is C++23 "deducing this" and what does it solve?
C++23 lets a member take this as an explicit parameter: void f(this Self&& self, ...). Templated on value category and cv-qualification — replaces four overloads. Enables CRTP without inheritance.
Common mistakes
- ✗Trying to use
this->inside a deducing-this function —thisis gone, useself.instead - ✗Marking the function
virtual— incompatible - ✗Forgetting to forward: write
std::forward<Self>(self).memberto preserve value category
Follow-up questions
- →How does deducing-this remove the need for CRTP?
- →Why can't a deducing-this function be virtual?