Question: Does the following compile?
int func() { int (func); return func; }
The answer, rather appallingly, is: yes.
The compiler treats the highlighted line as a declaration of an int called func; which is uninitialised.
The compiler allows this, on the grounds that if it looks (vaguely) like a declaration it is a declaration. C++’s Most Vexing Parse is another wonderful example of such behaviour.
On a more practical note this could lead to some very subtle errors in multi-threaded code:
template <typename T> class Monitor { public: void add(const T& in_val) { std::unique_ptr<std::mutex>(mtx); // Acquire the lock // Add the data... } T get() { T out_val; std::unique_ptr<std::mutex>(mtx); // Acquire the lock // Get the data... return out_val; } private: // Some data storage std::mutex mtx; };
This looks OK, but will simply create a temporary std::unique_lock object called mtx – and then discard it. The actual mutex (Monitor::mtx) will never be locked. That is, despite appearances (and no compile errors or warnings), this code isn’t thread-safe.
Good luck debugging this!
A couple of notes on this issue:
- This code only compiles because a std::unique_lock has a default constructor. std::lock_guard doesn’t have a default constructor, so this would fail to compile; albeit with a less-than-obvious error.
- This problem is far less likely to occur with non-RAII-type classes, because you’ll get an error as soon as you attempt to call any method.
- This would fail to compile if you use brace-initialisation.
What’s the moral of this story? Always use brace-initialisation!
- Practice makes perfect, part 3 – Idiomatic kata - February 27, 2020
- Practice makes perfect, part 2– foundation kata - February 13, 2020
- Practice makes perfect, part 1 – Code kata - January 30, 2020
Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.
He specialises in C++, UML, software modelling, Systems Engineering and process development.
I just want to mention, that specifying -Wshadow in combination with -Werror prevents such pitfalls (at least with GCC and compatible compilers).
https://godbolt.org/g/Cq6Gk6
clang trunk gives an "vexing-parse" error: https://godbolt.org/g/9JYSu5.
You probably want to replace std::unique_ptr with std::unique_lock in your snippet.
Also, the second example is very convincing, because one must almost always give a name to a unique_lock, otherwise it is destroyed at the end of the expression, not at the end of scope.