Under which circumstances might std::unique_ptr::operator[] throw? - c++

I have an operator[] for my class and all it does is call std::unique_ptr::operator[] on a unique_ptr member. The relevant part is just this:
template <typename T> struct Foo {
T& operator [](const size_t pos) const noexcept
{
return data_[pos];
}
std::unique_ptr<T[]> data_;
};
I've marked the operator as noexcept. However, unique_ptr::operator[] is not noexcept. I am unable to find out why that is, and whether I can just assume that it will never throw. unique_ptr::operator[] itself does not list any exceptions in the documentation (cppreference and MSDN claim it does not define any list of exceptions it might throw.)
So I assume the missing noexcept might either be: a) a mistake, or b) the underlying datatype accessed by the operator might throw. Option a would be nice, since that would mean I can mark my own operator noexcept. Option b would be difficult to understand, since all the operator does it get a reference and it doesn't call anything.
So, long story short, is there any possibility of unique_ptr::operator[] ever throwing, and is it safe to call it from a noexcept function?

So, long story short, is there any possibility of unique_ptr::operator[] ever throwing
Yes. It will simply use [] on the pointer type that it has. And that could throw. Recall that, thanks to deleter gymnastics, the pointer type need not be an actual pointer. It could be a user-defined object type with its own operator[] overload that could throw on out-of-bounds use.

I thought I give it a try (I am not sure of the answer).
I went through the noexcept reference and my understanding is that noexcept is simply indicating that the function should not throw exception, so that the compiler could make more agressive optimisations.
As to why the unique_ptr::operator[] is not noexcept I guess that the one standardising imagined that some implementors may throw and some other may not.
My opinion is that unique_ptr::operator[] may be throwing depending on the implementation of unique_ptr (typically when the index is out of bound).
However depending of the context of your code you could make sure this won't happen and decide to specify noexcept for your operator[].

Related

Conditional noexcept: usecase

I am currently writing a public library for vector and matrix operations.
My library, as it stands now, only accepts arithmetic types and complex numbers (by using std::complex<T>).
Since I value-initialize an array of type T, where T might be std::complex<T>, and since std::complex<T>'s default constructor is not noexcept, I'm wondering whether it is the case to have my default constructor have a conditional noexcept for this single case, so that the constructor is noexcept only when T's default constructor is marked noexcept.
I don't know if that makes any sense; currently, I can't find a reason as to why this might be helpful. But on the other hand, I am completely unaware of what anyone using the library might do with it (they might, perhaps, store multiple Math::Vectors inside a std::vector too, I guess?).
So long story short: I'm wondering whether something like this would make sense, to cover the case where std::complex<T> is passed:
private:
T math_vector[Size]{}; // T might be std::complex
public:
constexpr Vector() noexcept(std::is_nothrow_default_constructible_v<T>) = default;

What can (and what can't) throw an exception in c++?

Is there complete list (maybe recursively defined) of "code statements" that can lead to exception in c++? Something like this:
1) throw statement (naturally)
2) calls to new
3) calls to any functions from standard library that are documented to be able to throw.
4) calls to any user-defined functions (including constructors) that contain operations from 1-3.
5) Something else? Allocating local objects on stack, operations on built-in types, dereferencing pointers, type casts - are they able to throw?
6) Everything else is exception-free.
By exception-free I don't mean operations that are always successful. Dereferencing a pointer surely isn't. But still it does not make sense to wrap it in try-catch block, think about exception-safety of function dereferencing a pointer, etc. So the code that either successful or leads to undefined behaviour can be considered as exception-free.
Upd. Despite my last paragraph I still got a comment that undefined behaviour also can throw, so let me explain what I mean. Consider following code:
void bar();
Class C{
...
public:
foo() {
something_that_breaks_class_invariants;
bar();
something_that_restores_class_invariants;
}
}
If I correctly understand what exception safety is about, then if bar() can throw exception, this code is bad. I should change the order of statements, or I should wrap bar() in try-catch block, restore class invariants and propagate exception further.
But if bar() either successfully returns or leads to undefined behaviour (because, I don't know, something else is broken), then foo() is ok. foo() can't do anything and shouldn't care about possible undefined behaviour of bar(). In this sense bar() is exception-free, can be marked noexcept, etc.
So my question is: what kinds of statements can be in bar() to consider it exception-free is this sense?
Yes, the list of things that can throw in C++ can be exhaustively defined.
throw expression
new can throw bad_alloc
dynamic_cast can throw bad_cast
typeid can throw bad_typeid
any call to a function that is not noexcept or throw()
The last point also applies to all the implicit function calls of C++: default/copy/move constructors, overloaded operators, destructors (note that those default to noexcept) and conversion operators.
If you are ever in doubt about a particular expression, you can use the noexcept operator to have the compiler tell you whether it's theoretically possible for the expression to throw.
Your list is almost complete. There's indeed one cast that can throw: dynamic_cast<Derived&>. That's because there are no null references. dynamic_cast<Derived*> returns a null pointer instead of throwing.
In all cases, exceptions are thrown when evaluating expressions. Variable definitions may only throw when they contain an expression, e.g. in an initializer or inside a constructor.
The "Undefined Behavior" bit is a red herring. You simply can't reason about a C++ program that has Undefined Behavior, not even about what might have happened prior to that UB. That means we're assuming no UB whenever we are reasoning about defined C++ behavior.
The answer is simple: Any statement that uses an overloadable operator or function that cannot be proven not to throw, can throw. Here are some examples:
template <class T> T foo(const T& arg) { return arg; } //can throw (copy constructor!)
template <class T> void foo(T a, T b) { a+b; } //can throw (+ is overloadable)
template <class T>
void foo(T iter, T end) {
for(; iter < end; iter++) { //both the < and the ++ operator can throw
iter->bar(); //even the -> operator is overloadable and can throw
}
}
In short, whenever you do not have knowledge of the involved types, as is generally the case with templates, you pretty much have to call any statement a throwing statement.
Afaik, the only valid defense against this not to allow exceptions in the first place.

unordered_set::find and noexcept

I have the following unordered_set:
class ArtifactImpl{...};
class ArtifactSetKeyOps
{
public:
std::size_t operator()(const ArtifactImpl& artifact) const noexcept;
bool operator()(const ArtifactImpl& lhs, const ArtifactImpl& rhs) const noexcept;
};
std::unordered_set<ArtifactImpl,
ArtifactSetKeyOps,ArtifactSetKeyOps> artifactSet_;
Note that my hash and predicate template argument/s have a noexcept specification.
Would it be possible for unordered_set::find to throw?
I'm aware that find isn't marked noexcept, but in practice find should not perform allocations...
Would it be acceptable to call find from a noexcept marked function?
e.g:
const ArtifactImpl& foo() noexcept
{
auto pos = artifactSet_.find(key);
//etc...
}
Even though foo() here above goes against find's noexcept specifications, I'm wanting to know whether the specification holds practically iff one provides one's own noexcept hash and comparison.
tl;dr: noexcept seems useful only for move constructor/assignment operator.
If you would dare to accept cplusplus.com or even me as sufficient authority: it does not throw any exception, unlessan aspect does - such as the allocator or a comparator throws, nothing throws.
(I should probably go dig out the relevant ISO passages now...)
However, as to my understanding, noexcept isn't particulary useful here:
Andrzej has a detailed explanation on his blog, from which I (mis)quote the relevant parts here:
noexcept doesn't buy much if the implementation throws despite the specification
it doesn't help with implementing a no-throw exception safety guarantee
and it is unlikely to enable significant optimizations except in one case, related to move semantics:
for certain functions, like vector<T>::push_back using move constructors/assignments of T instead of copy constructors/assignments can dramatically increase performance. However, if this move constructor/assignment can potentially throw, push_back would loose the strong exception safety guarantee. In order to take an advantage of the move operations (where possible) and at the same time keep the strong guarantee, there needs to be a way to determine if a given move operation is potentially throwing or not, and use it or alternatively fall back to good old copying. This is exactly what function std::move_if_noexcept does, but it needs T’s operations to be marked as noexcept where appropriate.
It could throw if you hash function (which it must call) throws. For that reason alone I wouldn't use it. But more importantly, I wouldn't use the noexcept specifier because it goes against the find function's specifications.

Why does std::unique_ptr operator* throw and operator-> does not throw?

In the C++ standard draft (N3485), it states the following:
20.7.1.2.4 unique_ptr observers [unique.ptr.single.observers]
typename add_lvalue_reference<T>::type operator*() const;
1 Requires: get() != nullptr.
2 Returns: *get().
pointer operator->() const noexcept;
3 Requires: get() != nullptr.
4 Returns: get().
5 Note: use typically requires that T be a complete type.
You can see that operator* (dereference) is not specified as noexcept, probably because it can cause a segfault, but then operator-> on the same object is specified as noexcept. The requirements for both are the same, however there is a difference in exception specification.
I have noticed they have different return types, one returns a pointer and the other a reference. Is that saying that operator-> doesn't actually dereference anything?
The fact of the matter is that using operator-> on a pointer of any kind which is NULL, will segfault (is UB). Why then, is one of these specified as noexcept and the other not?
I'm sure I've overlooked something.
EDIT:
Looking at std::shared_ptr we have this:
20.7.2.2.5 shared_ptr observers [util.smartptr.shared.obs]
T& operator*() const noexcept;
T* operator->() const noexcept;
It's not the same? Does that have anything to do with the different ownership semantics?
A segfault is outside of C++'s exception system. If you dereference a null pointer, you don't get any kind of exception thrown (well, atleast if you comply with the Require: clause; see below for details).
For operator->, it's typically implemented as simply return m_ptr; (or return get(); for unique_ptr). As you can see, the operator itself can't throw - it just returns the pointer. No dereferencing, no nothing. The language has some special rules for p->identifier:
§13.5.6 [over.ref] p1
An expression x->m is interpreted as (x.operator->())->m for a class object x of type T if T::operator->() exists and if the operator is selected as the best match function by the overload resolution mechanism (13.3).
The above applies recursively and in the end must yield a pointer, for which the built-in operator-> is used. This allows users of smart pointers and iterators to simply do smart->fun() without worrying about anything.
A note for the Require: parts of the specification: These denote preconditions. If you don't meet them, you're invoking UB.
Why then, is one of these specified as noexcept and the other not?
To be honest, I'm not sure. It would seem that dereferencing a pointer should always be noexcept, however, unique_ptr allows you to completely change what the internal pointer type is (through the deleter). Now, as the user, you can define entirely different semantics for operator* on your pointer type. Maybe it computes things on the fly? All that fun stuff, which may throw.
Looking at std::shared_ptr we have this:
This is easy to explain - shared_ptr doesn't support the above-mentioned customization to the pointer type, which means the built-in semantics always apply - and *p where p is T* simply doesn't throw.
For what it's worth, here's a little of the history, and how things got the way they are now.
Before N3025, operator * wasn't specified with noexcept, but its description did contain a Throws: nothing. This requirement was removed in N3025:
Change [unique.ptr.single.observers] as indicated (834) [For details see the Remarks section]:
typename add_lvalue_reference<T>::type operator*() const;
1 - Requires: get() != 0nullptr.
2 - Returns: *get().
3 - Throws: nothing.
Here's the content of the "Remarks" section noted above:
During reviews of this paper it became controversial how to properly specify the operational semantics of operator*, operator[], and the heterogenous comparison functions. [structure.specifications]/3 doesn't clearly say whether a Returns element (in the absence of the new Equivalent to formula) specifies effects. Further-on it's unclear whether this would allow for such a return expression to exit via an exception, if additionally a Throws:-Nothing element is provided (would the implementor be required to catch those?). To resolve this conflict, any existing Throws element was removed for these operations, which is at least consistent with [unique.ptr.special] and other parts of the standard. The result of this is that we give now implicit support for potentially throwing comparison functions, but not for homogeneous == and !=, which might be a bit surprising.
The same paper also contains a recommendation for editing the definition of operator ->, but it reads as follows:
pointer operator->() const;
4 - Requires: get() != 0nullptr.
5 - Returns: get().
6 - Throws: nothing.
7 - Note: use typically requires that T be a complete type.
As far as the question itself goes: it comes down to a basic difference between the operator itself, and the expression in which the operator is used.
When you use operator*, the operator dereferences the pointer, which can throw.
When you use operator->, the operator itself just returns a pointer (which isn't allowed to throw). That pointer is then dereferenced in the expression that contained the ->. Any exception from dereferencing the pointer happens in the surrounding expression rather than in the operator itself.
Frankly, this just looks like a defect to me. Conceptually, a->b should always be equivalent to (*a).b, and this applies even if a is a smart pointer. But if *a isn't noexcept, then (*a).b isn't, and therefore a->b shouldn't be.
Regarding:
Is that saying that operator-> doesn't actually dereference anything?
No, the standard evaluation of -> for a type overloading operator-> is:
a->b; // (a.operator->())->b
I.e. the evaluation is defined recursively, when the source code contains a ->, operator-> is applied yielding another expression with an -> that can itself refer to a operator->...
Regarding the overall question, if the pointer is null, the behavior is undefined, and the lack of noexcept allows an implementation to throw. If the signature was noexcept then the implementation could not throw (a throw would be a call to std::terminate).

Passing temporaries as LValues

I'd like to use the following idiom, that I think is non-standard. I have functions which return vectors taking advantage of Return Value Optimization:
vector<T> some_func()
{
...
return vector<T>( /* something */ );
}
Then, I would like to use
vector<T>& some_reference;
std::swap(some_reference, some_func());
but some_func doesn't return a LValue. The above code makes sense, and I found this idiom very useful. However, it is non-standard. VC8 only emits a warning at the highest warning level, but I suspect other compilers may reject it.
My question is: Is there some way to achieve the very same thing I want to do (ie. construct a vector, assign to another, and destroy the old one) which is compliant (and does not use the assignment operator, see below) ?
For classes I write, I usually implement assignment as
class T
{
T(T const&);
void swap(T&);
T& operator=(T x) { this->swap(x); return *this; }
};
which takes advantage of copy elision, and solves my problem. For standard types however, I really would like to use swap since I don't want an useless copy of the temporary.
And since I must use VC8 and produce standard C++, I don't want to hear about C++0x and its rvalue references.
EDIT: Finally, I came up with
typedef <typename T>
void assign(T &x, T y)
{
std::swap(x, y);
}
when I use lvalues, since the compiler is free to optimize the call to the copy constructor if y is temporary, and go with std::swap when I have lvalues. All the classes I use are "required" to implement a non-stupid version of std::swap.
Since std::vector is a class type and member functions can be called on rvalues:
some_func().swap(some_reference);
If you don't want useless copies of temporaries, don't return by value.
Use (shared) pointers, pass function arguments by reference to be filled in, insert iterators, ....
Is there a specific reason why you want to return by value?
The only way I know - within the constraints of the standard - to achieve what you want are to apply the expression templates metaprogramming technique: http://en.wikipedia.org/wiki/Expression_templates Which might or not be easy in your case.