Why is std::unique_ptr::reset() always noexcept? - c++

A recent question (and especially my answer to it) made me wonder:
In C++11 (and newer standards), destructors are always implicitly noexcept, unless specified otherwise (i.e. noexcept(false)). In that case, these destructors may legally throw exceptions. (Note that this is still a you should really know what you are doing-kind of situation!)
However, all overloads of
std::unique_ptr<T>::reset() are declared to always be noexcept (see cppreference), even if the destructor if T isn't, resulting in program termination if a destructor throws an exception during reset(). Similar things apply to std::shared_ptr<T>::reset().
Why is reset() always noexcept, and not conditionally noexcept?
It should be possible to declare it noexcept(noexcept(std::declval<T>().~T())) which makes it noexcept exactly if the destructor of T is noexcept. Am I missing something here, or is this an oversight in the standard (since this is admittedly a highly academic situation)?

The requirements of the call to the function object Deleter are specific on this as listed in the requirements of the std::unique_ptr<T>::reset() member.
From [unique.ptr.single.modifiers]/3, circa N4660 §23.11.1.2.5/3;
unique_ptr modifiers
void reset(pointer p = pointer()) noexcept;
Requires: The expression get_deleter()(get()) shall be well formed, shall have well-defined behavior, and shall not throw exceptions.
In general the type would need to be destructible. And as per the cppreference on the C++ concept Destructible, the standard lists this under the table in [utility.arg.requirements]/2, §20.5.3.1 (emphasis mine);
Destructible requirements
u.~T() All resources owned by u are reclaimed, no exception is propagated.
Also note the general library requirements for replacement functions; [res.on.functions]/2.

std::unique_ptr::reset does not invoke destructor directly, instead it invokes operator () of the deleter template parameter (which defaults to std::default_delete<T>). This operator is required to not throw exceptions, as specified in
23.11.1.2.5 unique_ptr modifiers [unique.ptr.single.modifiers]
void reset(pointer p = pointer()) noexcept;
Requires: The expression get_deleter()(get()) shall be well-formed, shall have >well-defined behavior, and shall not throw exceptions.
Note that shall not throw is not the same as noexcept though. operator () of the default_delete is not declared as noexcept even though it only invokes delete operator (executes delete statement). So this seems to be a rather weak spot in the standard. reset should either be conditionally noexcept:
noexcept(noexcept(::std::declval<D>()(::std::declval<T*>())))
or operator () of the deleter should be required to be noexcept to give a stonger guarantee.

Without having been in the discussions in the standards committee, my first thought is that this is a case where the standards committee has decided that the pain of throwing in the destructor, which is generally considered undefined behaviour due to the destruction of stack memory when unwinding the stack, was not worth it.
For the unique_ptr in particular, consider what could happen if an object held by a unique_ptr throws in the destructor:
The unique_ptr::reset() is called.
The object inside is destroyed
The destructor throws
The stack starts unwinding
The unique_ptr goes out of scope
Goto 2
There was to ways of avoiding this. One is setting the pointer inside of the unique_ptr to a nullptr before deleting it, which would result in a memory leak, or to define what should happen if a destructor throws an exception in the general case.

Perhaps this would be easier to explain this with an example. If we assume that reset wasn't always noexcept, then we could write some code like this would cause problems:
class Foobar {
public:
~Foobar()
{
// Toggle between two different types of exceptions.
static bool s = true;
if(s) throw std::bad_exception();
else throw std::invalid_argument("s");
s = !s;
}
};
int doStuff() {
Foobar* a = new Foobar(); // wants to throw bad_exception.
Foobar* b = new Foobar(); // wants to throw invalid_argument.
std::unique_ptr<Foobar> p;
p.reset(a);
p.reset(b);
}
What do we when p.reset(b) is called?
We want to avoid memory leaks, so p needs to claim ownership of b so that it can destroy the instance, but it also needs to destroy a which wants to throw an exception. So how and we destroy both a and b?
Also, which exception should doStuff() throw? bad_exception or invalid_argument?
Forcing reset to always be noexcept prevents these problems. But this sort of code would be rejected at compile-time.

Related

Thread-safe stack implementation

"C++ Concurrency in Action second edition" has an example of thread-safe stack implementation, and the following is the code for pop:
template <typename T>
std::shared_ptr<T> threadsafe_stack<T>::pop()
{
std::lock_guard<std::mutex> lock(m);
// `data` is a data member of type `std::stack<T>`
if(data.empty()) throw empty_stack(); // <--- 2
std::shared_ptr<T> const res(
std::make_shared<T>(std::move(data.top()))); // <--- 3
data.pop(); // <--- 4
return res;
}
The book has the following explanation for this code:
The creation of res (3) might throw an exception, though, for a couple of reasons: the call to std::make_shared might throw because it can’t allocate memory for the new object and the internal data required for reference counting, or the copy constructor or move constructor of the data item to be returned might throw when copying/moving into the freshly- allocated memory. In both cases, the C++ runtime and Standard Library ensure that there are no memory leaks and the new object (if any) is correctly destroyed. Because you still haven’t modified the underlying stack, you’re OK.
Does this explanation correct?
If data item's move constructor throws, and the data item is corrupted. Although this implementation avoid memory leak, the state of the stack has been damaged/modified (not strong exception safe), and latter read will get corrupted data. Right?
So it seems that this implementation works, only if the move constructor doesn't throw (or at least, even if it throws, it keeps the data item unmodified)?
It is assumed that the if the move constructor of T throws an exception it will make sure that the original object remains in its original state.
That is a general exception guarantee that move operations should satisfy, otherwise it is trivially impossible to make any guarantees about exception safety when the move operation is used.
If that is not supposed to be a precondition on the element type, the copy constructor can be used instead of the move constructor if the move constructor is not noexcept. The copy constructor should never modify the original object. There is std::move_if_noexcept as a replacement for std::move in the standard library to implement this behavior.
Obviously if the type is not copyable and the move constructor not noexcept we are back again at the original problem and then it will simply be impossible to make exception guarantees if T's move constructor doesn't make any.

Requirements on returned type that may have some member functions SFINAE'd away in the function's translation unit?

Refining from Why is the destructor implicitly called?
My understanding of calling convention is that functions construct their result where the caller asked them to (or in a conventional place?). With that in mind, this surprises me:
#include <memory>
struct X; // Incomplete type.
// Placement-new a null unique_ptr in-place:
void constructAt(std::unique_ptr<X>* ptr) { new (&ptr) std::unique_ptr<X>{nullptr}; }
// Return a null unique_ptr:
std::unique_ptr<X> foo() { return std::unique_ptr<X>{nullptr}; }
https://godbolt.org/z/rqb1fKq3x
Whereas constructAt compiles, happily placement-newing a null unique_ptr<X>, foo() doesn't compile because the compiler wants to instantiate unique_ptr<X>::~unique_ptr(). I understand why it can't instantiate that destructor (because as far as the language is concerned, it needs to follow the non-nullptr branch of the d'tor that then deletes the memory [https://stackoverflow.com/questions/28521950/why-does-unique-ptrtunique-ptr-need-the-definition-of-t]). Basically without a complete X, the unique_ptr's destructor is SFINAE'd away (right?). But why does a function returning a value have to know how to destruct that value? Isn't the caller the one that will have to destruct it?
Clearly my constructAt and foo functions aren't morally equivalent. Is this language pedantry, or is there some code path (exceptions?) where foo() would have to destruct that value?
In your specific case there is no way that the destructor may be invoked. However, the standard specifies the situations in which the destructor is potentially invoked in more general terms. If a destructor is potentially invoked it requires a definition (even if there is no path that could call it) and this will therefore cause implicit instantiation which fails in your case since instantiation of the std::unique_ptr<X> destructor requires X to be complete.
In particular the destructor is potentially invoked for every result object in a return statement.
I think the reason for this choice is described in CWG issue 2176: In general there may be local variables and temporaries in the function which are destroyed after the result object of the return statement has been constructed. But if the destruction of one of these objects throws an exception, then the already constructed result object should also be destroyed. This requires the destructor to be defined.
CWG issue 2426 then made the destructor potentially invoked even if there is no actual invocation due to the above reasoning, in line with implementations. I assume this choice was made simply because it doesn't require any additional decision making on the compiler's part and was already implemented.

is std::move() the same as using reset() and release() together? [duplicate]

For std::unique_ptrs p1 and p2, what are differences between std::move() and std::unique_ptr::reset()?
p1 = std::move(p2);
p1.reset(p2.release());
The answer should be obvious from the standard's specification of move assignment in [unique.ptr.single.assign]/2:
Effects: Transfers ownership from u to *this as if by calling reset(u.release()) followed by an assignment from std::forward<D>(u.get_deleter()).
Clearly move assignment is not the same as reset(u.release()) because it does something additional.
The additional effect is important, without it you can get undefined behaviour with custom deleters:
#include <cstdlib>
#include <memory>
struct deleter
{
bool use_free;
template<typename T>
void operator()(T* p) const
{
if (use_free)
{
p->~T();
std::free(p);
}
else
delete p;
}
};
int main()
{
std::unique_ptr<int, deleter> p1((int*)std::malloc(sizeof(int)), deleter{true});
std::unique_ptr<int, deleter> p2;
std::unique_ptr<int, deleter> p3;
p2 = std::move(p1); // OK
p3.reset(p2.release()); // UNDEFINED BEHAVIOUR!
}
The first one is capable of warning you if there is a destructor mismatch, for example. In addition, release() is a very dangerous function, and your trivial example is correct but many other uses are not. It's best to simply never, ever use this function.
The second version might not be exception safe, I think. It is equivalent to:
auto __tmp = p2.release();
p1.reset(__tmp);
Thus if the call to std::unique_ptr::reset throws (which may be the case if the deletion of the managed object throws), then you have an unreferred object which won't get destroyed ever. In case of the move assignment, the std::unique_ptr can (and should) wait with the actual move until p1's original object has been destroyed properly.
But note, that this is only a problem if the destructor of the managed object could throw, which is in nearly all cases wrong in itself, or if you use a custom deleter which might throw. So in practice there isn't usually any behavioural difference between the two code snippets.
EDIT: In the end Jonathan points out in his comment, that the custom deleter is required by the standard not to throw, which indeed makes the throwing of std::unique_ptr::reset pretty unlikely/non-conformant. But he also points out that there is another difference, in that only a move assignment also moves any custom deleters, which he has also written an answer for.
But disregarding actual resulting behaviour, there is a huge conceptual difference between the two. If a move assignment is appropriate, then do a move assignment and try not to emulate it by some other code. In fact I cannot image any reason to replace the first code snippet one-to-one by the second. DeadMG is right in that std::unique_ptr::release should only be used if you really know what you're doing and in which context you're messing with unmanaged dynamic objects.

Why smart pointer templates need to disable exception throwing in constructor

I find such question while reading the Smart Pointer Template Classes in C++ primer plus.
The book gives an example of how auto_ptr class is implemented, like this,
template<class X> class auto_ptr {
public:
explicit auto_ptr(X* p = 0) throw();
...};
throw() at the end of constructor means this constructor doesn't throw an exception.
I know this is deprecated, but I do not know why it needs to disable its exception throwing.
throw() doesn't disable exception throwing. It simply says that the function does not throw any exceptions. That's a statement about the code in the constructor: there's nothing there that will throw an exception that escapes from the constructor. That means that either the code in the constructor doesn't throw any exceptions (for example, int i = 3; will not throw an exception), or that anything that does throw an exception is enclosed in a try block whose catch clauses don't throw anything.
Because if the constructor of auto_ptr would throw exceptions, the pointer might be lost and then cause memory leak. For example:
auto_ptr<int> ap(new int);
The memory allocated couldn't be deallocated again if the constructor failed.
They are declaring an intent/promise not to throw exceptions - they still can, but will be smacked down hard by the runtime if they do.
The reason to use noexcept over throw() is mostly academic.

Returning by value calls the copy constructor?

template<class T>
T Stack<T>::pop()
{
if (vused_ == 0)
{
throw "Popping empty stack";
}
else
{
T result = v_[used_ - 1];
--vused_;
return result;
}
}
I didn't understand all of it, or rather I understood none of it, but it was said that this code doesn't work, because it returns by value, I am guessing he was referring to result, and that calls the copy constructor and I have no idea how that's even possible. Can anyone care to explain?
Unlike the code in the question's example, std::stack<T>::pop does not return a value.
That's because if the item type needs to be copied, and the copying throws, then you have an operation failure that has changed the state of the object, with no means of re-establishing the original state.
I.e. the return-a-value-pop does not offer a strong exception guarantee (either succeed or no change).
Similarly, throwing a literal string is unconventional to say the least.
So while the code doesn't have any error in itself (modulo possible typing errors such as vused_ versus v_ etc.), it's weak on guarantees and so unconventional that it may lead to bugs in exception handling elsewhere.
A different viewpoint is that the non-value-returning pop of std::stack is impractical, leading to needlessly verbose client code.
And for using a stack object I prefer to have a value-returning pop.
But it's not either/or: a value-returning convenience method popped can be easily defined in terms of pop (state change) and top (inspection). This convenience method then has a weaker exception guarantee. But the client code programmer can choose. :-)
An improvement within the existing design would be to support movable objects, that is, replace
return result;
with
return move( result );
helping the compiler a little.
↑ Correction:
Actually, the above deleted text has the opposite effect of the intended one, namely, it inhibits RVO (guaranteeing a constructor call). Somehow my thinking got inverted here. But as a rule, don't use move on a return expression that is just the name of a non-parameter automatic variable, because the default is optimization, and the added move can not improve things, but can inhibit an RVO optimization.
Yes, returning by value formally calls the copy constructor. But that's not a problem at all, because in practice, compilers will typically be able to optimize away the additional copy. This technique is called "Return-Value Optimization".
More than the return statement (which can work if the class is movable but not copyable, e.g. you can return std::unique_ptrs), the problem is the copy you do here:
T result = v_[used_ - 1];
To make this copy possible, the type T must be copyable (e.g. T should have public copy constructor - required by the above statement - and copy assignment operator=).
As a side note, throwing a string is really bad: you should throw an exception class, e.g.
throw std::runtime_error("Popping empty stack.");
or just define an ad hoc class for this case and throw it, e.g.:
class StackUnderflowException : public std::runtime_error
{
public:
StackUnderflowException()
: std::runtime_error("Popping empty stack.")
{ }
};
....
throw StackUnderflowException();