
C++ Programming Questions to Ask on Interview (2017) - sytelus
https://tests4geeks.com/cpp-interview-questions/
======
lordnacho
I write c++ high frequency trading systems, but I think I'd have to look up
the answer to most of these. I've heard of all of them, though, and I know
roughly what the point of them is.

Your problem when interviewing someone is that in real life, you are relying
on the person being quick to learn, since they are unlikely to have the answer
in cache. Maybe if they'd just read Stroustrup they'd know some of the
specifics, but most coders would know what the point was and google it.

You have to wonder that a fair question is. I supposed you should know of a
bunch of things about c++ (why you're using it, major features like
templates), without knowing all the specifics (SFINAE/CRTP examples). For
instance it's fair to ask someone what a memory barrier is. But not to list
all the varieties (relaxed, acquire, etc).

I've hired a number of c++ devs in the last few months, and all of them have
turned out fine. I never asked anything particularly specific like this list
of questions, just poked around the very large house that is c++. What does
the candidate think changed in c++11? Why are we using it for HFT? What kind
of thing will slow us down?

I think if I'd asked these questions I'd have dropped a lot of perfectly fine
candidates.

~~~
Uberphallus
Regarding HFT, how much is C++ and how much is FPGA? I always thought even
CPUs often turn out too slow for that.

~~~
steveklabnik
There’s HFT systems in Java, and OCaml. I’m not an _expert_ in these domains
but since markets are only open for certain hours of the day, you can pre-
allocate enough memory and just not collect till after the bell rings. In my
understanding.

------
ajross
Some of this is straightforward. The rest.... eesh.

I mean, yeah, I guess you can quiz your candidates on their knowledge of stuff
like enable_if and ability to write templates based on traits like is_integer,
but don't expect that to be a good filter for anything but language minutiae.

And question 10... what on earth is a "max heap". I'm gathering from context
that they are asking about a heap in the sense of the packed tree data
structure. I've used those, and written those on multiple occasions, so it's
not an impractical subject. But (1) if you asked me the question the way it's
phrased here I'd look at you blankly and wonder if you were talking about some
kind of custom allocator, and (2) _they don 't even want the candidate to talk
about the data structure!_ They just want a class definition written for the
thing with a handful of obvious interfaces (add, get maximum, y'know... it's
just a heap). How is that useful to anyone?

~~~
flafla2
A "max heap" is a heap that has O(1) access of the maximum priority element,
as opposed to a "min heap" which returns the lowest priority. It's largely a
semantic distinction, as a max heap = a min heap with negative keys. Also a
heap isn't just a packed tree, it's referring specifically to a heap data
structure [1] (which is often implemented using a packed tree, but not
necessarily).

This is very common terminology, and honestly I'd expect any CS grad past an
sophomore undergraduate level to understand exactly what a max heap is,
especially in an interview setting.

[1]
[https://en.wikipedia.org/wiki/Heap_(data_structure)](https://en.wikipedia.org/wiki/Heap_\(data_structure\))

~~~
ajross
> I'd expect any CS grad past an sophomore undergraduate level to understand
> exactly what a max heap is

Never once heard the term, and I've written several heap-based priority queues
over the course of more than two decades of professional work.

So... yeah, I'm too dumb for that filter I guess.

~~~
banachtarski
Surely you've heard the distinction between the max-heap and a min-heap
before...

And honestly, I'm sure if you asked what it was in an interview, you would get
a straight answer and not be dinged for it. I would filter based on your
saltiness level though.

~~~
dev_dull
Sounds like their would be mutual filtering.

------
tragomaskhalos
Slightly off topic, but a candidate applying for a _general_ programming
position who claims knowledge of C++ in their CV is presenting an absolute
gift for interviewers: because, if they actually do know their C++, then they
are likely to be a strong candidate overall; conversely, if they are
bullsh1tting you it is very easy to find this out quickly.

True story: once interviewed a guy who claimed to know C++; set him a
challenge that he couldn't answer, simplified it a bit, still couldn't answer,
we iterated down until I discovered he didn't even know how to declare an int
variable. Turns out he had been a _tester_ on a system _written_ in C++ ...

~~~
xsr2
I'm a C# developer and I've been strongly considering learning C++ in detail
because I feel like it would make me a better programmer in general. However
getting started with such a complex language seems daunting. Anyone know where
one should start? C++11? C++14? Something even older?

~~~
MauranKilom
I very much agree that it helps you understand the "programming stack" much
more intimately. If that's what you are after, you'll also have the parts that
are relevant to you covered relatively quickly (compared to fully "knowing"
C++ which would probably take at least a decade).

I also agree with the other reply that the strongest learning experience is
basically in the chronology of the language development. It takes running into
a case where they are the best solution to really grasp "why include guards?",
"why templates?", "why smart pointers?", "why iterators?" and so on, and you
also gain a lot of appreciation for what compilers in other languages do for
you behind the scenes.

I also highly recommend [https://godbolt.org/](https://godbolt.org/), which
allows you to see what machine code is produced in the end. Seeing all the
template magic getting distilled to clean asm is quite amazing.

------
slavik81
> cannot return 0 from a void [main] function

"A conforming implementation may provide more versions of main(), but they
must all have return type int." ~ Bjarne Stroustrup -
[http://www.stroustrup.com/bs_faq2.html#void-
main](http://www.stroustrup.com/bs_faq2.html#void-main)

------
cjensen
So in the first question, they test to make sure the candidate knows the
minutia that std::unique_ptr<> has no copy constructor.

And in the followup, they assume the candidate will just assume that
"SearchQuery", whose header is not shown, _will_ have a copy constructor.

Just no.

~~~
slavik81
I looked at that first one and didn't realize it wouldn't compile simply
because I would never write something like that anyways. That function should
just take an int&.

However, understanding that SearchQuery is broken is pretty important.
Breaking the Rule-of-3 is probably the #1 mistake made by junior C++
developers trying to write object-oriented code, and the consequences are
pretty nasty. You'll get corruption and crashes from use-after-free and
double-free errors and nothing will make sense because the problem will move
around as you create temporary copies while trying to debug.

That being said, I wasn't familiar with that sort of stuff when I was first
hired as a C++ developer. I was very fortunate to start my career with a
company that was willing to hire people and train them.

~~~
lasagnaphil
Right now one of the major gripes I have about Modern C++ is RAII / Rule-of-
five. After going through all of this stuff I felt it brought too much
complexity for what it tried to accomplish...

RAII basically assumes that your objects are always going to be in the init-
use-free pattern (you read a file, you use it, and close it after you’re
finished) In reality not all things work this way, as objects have various
different states according to their state machines and will use and free
resources depending on that state. But in Modern C++ you’re forced to think
only in the on/off paradigm, which results in lots of small unnecessary
classes with their own on/off state. (If you try graphics programming you’ll
understand right away...) Now when you add rule-of-five this becomes a
monstrosity to handle, as you have to ensure about memory copying/moving for
every little RAII class you make.

So why not let normal assignment be shallow copy by default, but have an
explicit copy() method? Why is everything so implicit in Modern C++ where you
can’t assure that by simply looking at a = b you can’t tell how memory is
allocated/freed...

Nowadays I have lost hope in Modern C++ and are looking for alternatives: Rust
(complex but at least memory safe) Orthodox C++ (not memory safe but at least
sane for graphics/game programming), or Jai/Zig/other experimental lnguages
(which claim to be a better C)

~~~
slavik81
I'm a graphics programmer myself. To be honest, my strategy for OpenGL-related
resources is to allocate everything I'll ever need at program start and leak
everything at program shutdown. It's simple and efficient and all copies are
indeed shallow. However, if I was writing more generic code, I would probably
make extensive use of movable but non-copyable containers for my OpenGL
resources.

You're right that the RAII model sometimes feels a bit limiting for the
complexity sometimes found in graphics, but I think part of that was self-
inflected by our API design. That seems to be changing. The best-looking
technique I've seen for handling Vulkan/DX12 is to make your program more like
a compiler. You take a scene as input and you compile it into a command buffer
to give the GPU. Your resource handling for stuff like texture slots thus
looks more like register allocation. State shadowing is a trivial optimization
pass when your GPU code is data.

The problem is that when you write RAII classes for GPU resources in the
traditional way, C++ sees the GPU state through a tiny pinhole and thus can't
optimize very well. When you treat your GPU commands as a program that your
C++ program is building, you have more global information about GPU state
available, which allows you to make more efficient choices.

------
jeorgun
These questions seem pretty good!

In the max heap question, I'd argue that add _should_ take a parameter by
value, rather than a const reference, since this prevents having to needlessly
copy temporaries (and is less verbose than overloading for T&&).

List of iterator categories is missing input iterators.

------
olliej
My reading of their moving assignment operator is that it is wrong, or at the
very least unclear.

Basically they do std::swap on query field. In that case you are relying on
the destructor of the moved object performing the delete (if necessary). I
would argue that that falls into the “too clever” spectrum, and also kind of
assumes that no one is going to refactor the destructor at some point in such
a way that some other property will result in them incorrectly thing that the
query is null, and so leaking it.

Having now read through this, I agree with others assessment - these questions
are questionable at best. What is it they’re proven to do?

~~~
banachtarski
I think relying on the destructor of the moved-from object running feels
idiomatic to me. That's what the destructor is there for after all.

~~~
olliej
The destructor deleting its /own/ data. But here we are moving a bunch of the
fields (and so clearing them) but swapping one of them. That seems like bad
coding, and doesn’t match any idiom I’ve seen :-/

~~~
jcelerier
for vectors it makes std::move a very fast operation

~~~
olliej
std::move on vector zeroes out the source element array.

e.g. (pseudo-ish code)

operator=(vector&& foo) {

delete [] elements;

elements = foo.elements;

foo.elements = nullptr;

}

what the example code they have as an answer does is something morally
equivalent to this:

operator=(vector&& foo) {

swap(elements, foo.elements);'

}

relying on the knowledge that because it's being moved the ~vector is going to
be called immediately afterwards.

This strikes me as being "clever" in the "potentially confusing code, for no
real reason" sense

~~~
gpderetta
actually that move assignment operator is not ideal. The foo parameter won't
be destroyed immediately but only at the end of whatever scope (if any) it was
at the call site.

The idiomatic copy and swap is like this:

T& operator =(T x) // note pass by value { std::swap( _this, x); return_ this;
}

This acts both as a normal and move assignment operator (if T is not copy
constructible);

edit: how do you get HN to show asterisks? There should be one before each
'this'

~~~
arh68
Prefix each line with 4 spaces:

    
    
        T& operator =(T x) // note pass by value
        { std::swap(*this, x); return *this; }

------
mnemotronic
Wow. Haven't gone near C++ for 12+ years. It done got weird.

~~~
rafiki6
This is why Go and Rust are coming into their own. C++ is a clusterf*ck of
complicated design decisions (note, I didn't say bad, just complicated). It's
certainly a powerful language for systems development but one has to wonder if
it needs to be. Anytime I see a language overdo it on the features, my sense
is that they need to step back and survey the landscape of developers before
they decide to add more stuff.

------
edflsafoiewq
I like that each question has a purpose. It's an interesting problem to
determine a (small) set of questions that "cover" the (large) space of C++
knowledge in the sense that if someone can answer them all, they likely have a
strong knowledge of the language.

> The interview candidate might think that returning a noncopiable object from
> a function is also a compiler error, but in this case it’s allowed, thanks
> to copy elision. You can ask the candidate under what conditions copy
> elision is performed.

This isn't copy elision. Copy elision isn't even permitted here because the
returned expression is the name of a function parameter. The two-stage
overload resolution that tries a move constructor first is defined in the same
section of the spec, [class.copy.elision], as copy elision but the rules are
different and it is always performed, even if the implementation doesn't do
copy elision.

------
partycoder
Writing safe, idiomatic C++ is hard.

Most C++ code found in college books is outdated and not idiomatic, driving
students away from the language.

But it is true that C++ can also be your worst nightmare if you don't have
discipline.

C++ will turn your bad coding habits into infinite frustration... and that is
great. C++ is the proof you really like programming.

------
BrissyCoder
I wrote C++ for relatively complex embedded guidance/positional apps for 5
years. I think I did it reasonably well - definitely better than 80% of my
colleagues. Since then (10 years or so) I've been C# almost exclusively.

This makes me hope I never have a need to go near that language ever again.

------
reacweb
I remember a time where mastering Stroustrup and the two Scoot Meyer was
enough to be considered an expert. Even as a Perl lover, the level of
intricacy of modern C++ makes me very reluctant to choose it on a new project.

------
alexeiz
These questions are quite simple. I wouldn't use them to find good C++
developers.

However, it's quite hard to come up with good C++ interview questions. You
want to test for fundamental knowledge of C++, for the practice of using C++
for developing code, but avoid impractical cases that nobody remembers in
their right mind.

Every interviewer has his own favorite C++ trick. The fact that an interviewee
doesn't know or use that trick doesn't mean that his C++ skills are
inadequate. It means that the interviewer's trick may not be as commonly used
as he may think.

------
Const-me
OK I'll bite.

1 - knowing this is 100% useless information, in reality I have a compiler to
tell me that.

2 - Seriously? new/delete in 2018? In very rare cases when I need a raw
pointer I call unique_ptr::release().

5 The question is so-so but the answer's wrong. Iterators don't need to point
anywhere. You can make an iterator generating values instead of referencing
container. Not necessarily a good idea but still.

6 Same. Just because it's not specified in C++ standard doesn't mean it's bad,
or should be avoided. E.g.

    
    
        const uint32_t n = 11;
        printf( "Eleven: %d\n", n );
    

is UB but the code is 100% fine to my standards. Stuff like OpenMP or
intrinsics is not specified either, does it mean we should avoid them?

8-9 IMO template metaprogramming should be avoided. If you want your template
data structure to behave differently for pointers and non-pointers, expose
another template argument for that, there's too many corner cases like handles
(technically pointers, logically values) and smart pointers (the opposite).

10 I don't think the sorted vector listed in "possible answer" is a max heap.

Conclusion: if the OP claims the questions are "Proven", I'm curious about
provement methodology.

~~~
MaxBarraclough
> is UB but the code is 100% fine to my standards

Strongly disagree - I'd never let that through code-review. When living in
C/C++ land, we should be extremely aware of the differences between the
integer types, and handle their conversions carefully and explicitly.

Given that the "%d" format-specifier maps to `int`, not to `int32_t` (let
alone `uint32_t`), that code really could wreck things if your platform has an
`int` type which isn't 32 bit -
[https://stackoverflow.com/a/3168293/](https://stackoverflow.com/a/3168293/)

~~~
Const-me
About int vs sint32, why exactly you should be aware about the differences?
The only cases when I encounter these differences, they look like broken C++
compiler:
[https://stackoverflow.com/q/50552347/126995](https://stackoverflow.com/q/50552347/126995)

Similar about %d and unsigned. Technically UB, practically works fine in 100%
of cases. If you ask “but how do you know it works?” I’ll answer “because I
read assembly”.

~~~
MaxBarraclough
> why exactly you should be aware about the differences?

Because they aren't assured to be the same. They might happen to be the same
on your current platform.

C++ isn't like Java. It's the programmer's job to be very aware of these sorts
of platform-specific points of variation.

I don't see why you'd deliberately write undefined behaviour when there's no
benefit to doing so. This isn't a purely academic concern - I've seen a
malformed printf call wreak absolute havoc, presumably because it was
stripping too much or too little data off the stack. Was a nightmare to track
it down.

I don't want to think about which particular families of dangerous type-based
mistakes are permissible in my codebase -- I'd rather write code which is
correct.

> The only cases when I encounter these differences, they look like broken C++
> compiler

If you write dangerous, non-portable code that invokes UB, you may well find
that it works on plenty of real-world platforms. Your code is still wrong. For
the specific case of assuming that `int` is 32-bit, you'll _probably_ get away
with this sort of clumsiness, yes, but 'ILP64' targets exist, even if they're
rare.

Additionally, using `int` as a dangerous alias for `int32_t` is not expressive
when it comes to readability.

> Technically UB, practically works fine in 100% of cases

UB can bite you in bizarre, unpredictable, intermittent ways, especially with
optimising compilers.

Why argue that a bad habit isn't _that_ bad, when you could just avoid doing
it?

> If you ask “but how do you know it works?” I’ll answer “because I read
> assembly”.

That's a very fragile assurance of correctness. Unless you explicitly embed
that assembly code, the compiler is free to generate different code at any
point in future. Can you guarantee that you'll never change or upgrade your
compiler, tweak its flags, or target another platform? Can you guarantee the
optimiser will never change its mind and generate different code for that
function?

~~~
Const-me
> They might happen to be the same on your current platform.

Good, and I’m not writing STL. I develop user-facing software, and I know the
platforms I support. It just happened so that in the last couple of decades,
all my current platforms have 32-bit integers, and I don’t expect that to
change within my remaining lifetime.

> It's the programmer's job to be very aware of these sorts of platform-
> specific points of variation.

Exactly. I’m writing platform-specific code in C++, and I don’t expect
compiler to interfere. When I want to abstract these things away I don’t code
C++, there’re better languages out there for such requirements.

> why you'd deliberately write undefined behaviour when there's no benefit to
> doing so.

Read the comments in the link I’ve posted. Because clang authors share your
opinion on the subject, I had to write a couple of pages of boilerplate code,
changing ~50 C++ functions for absolutely no value. That’s one of the reasons
why I prefer MSVC, by the way.

> ILP64 targets exist, even if they're rare.

Why should I care? As you see from this example, caring is not free, it has
costs.

> Additionally, using `int` as a dangerous alias for `int32_t` is not
> expressive when it comes to readability.

Additionally, typing int is 3 keypresses, typing int32_t is 8. Given how much
code I type every day, and how many integers are there in my code, the
difference is not insignificant. Even Nintendo did it better on CodeWarrior
toolchain, with u8, u32, u64, etc.

> UB can bite you in bizarre, unpredictable, intermittent ways, especially
> with optimising compilers.

I think you’re demonizing UB. Different cases are different. Some will crash
your app right away, the worst of them will silently give incorrect results
with 0.001% probability, but the code from my original comment is harmless.

Also C++ compilers aren’t perfect, couple times in my career I found bugs in
them. Many times, I found bugs in standard libraries or OSes. If you think
writing 100% standard compliant C++ code guarantees correct programs, I have
bad news for you.

> Can you guarantee that you'll never change or upgrade your compiler, tweak
> its flags

Do you have an example of specific compiler version, or flags, that will break
my code above, when targeting i386, amd64 or arm32 platforms?

------
crb002
Good way to weed out bad employeers who treat programmers like janitors with
no hope of career advancement.

------
liquidify
Why is checking for self assignment necessary?

~~~
banachtarski
There are pathological cases where due to layers of abstraction (in a data
structure say), an element gets moved to itself. The algorithm is unaware of
this but it happens anyways. If you fail to check for self assignment, you
will clear the "other" owning pointer/resource and this will result in a leak,
as well as resulting in an ill-formed object.

