This is a tough question and brings up some of the key issues covered in noexcept — what for? from
Andrzej's C++ blog which says, I will attempt to quote the minimum here (emphasis mine):
In this post I would like to share my observation on where using noexcept really adds value. It is less often than what one might expect, and it does not have that much to do with throwing or not throwing exceptions. The conclusion surprises me a bit, and I hesitate to present it because it is counter to the advice I hear from people I consider authorities on the subject.
and:
Given this negative attitude to noexcept, can it be considered useful at all? Yes. The noexcept feature was introduced very late into C++11 to address one particular issue with move semantics. It has been described here by Douglas Gregor and David Abrahams.
and then he goes on to given an unusual move assignment definition and argues that what we really want to convey is not that it does not throw exceptions but that it does not fail, but that is very difficult problem but it is out real intent:
[...]This is because the information that noexcept really is intended to
convey is that the function never fails; not that it never throws! We
can see above that a function can fail but still not throw, but it
still qualifies for noexcept(false). Perhaps the keyword should have
been called nofail. The never-fail guarantee cannot be checked by the
compiler (much like any other failure-safety guarantee), therefore the
only thing we can do is to declare it.
This is part of a more general observation, that what we are
interested in is really failure safety in program components rather
than exception safety. No matter if you use exceptions, error return
values, errno or what
ever else, the reasoning about basic (no leak, invariant preserved), strong (commit or rollback) and never-fail guarantee should still hold.
So if we take a similar position then, it would seem the answer is no, if not appropriate to use noexcept
and that seems to be where you are leaning. I don't think it is clear cut answer.
He also notes proposal N3248: noexcept
Prevents Library Validation. Which in turn was the basis for N3279: Conservative use of noexcept in the Library. This paper defines narrow and wide contracts as did N3248:
Wide Contracts
A wide contract for a function or operation does not
specify any undefined behavior. Such a contract has no preconditions:
A function with a wide contract places no additional runtime
constraints on its arguments, on any object state, nor on any external
global state. Examples of functions having wide contracts would be
vector::begin() and vector::at(size_type) . Examples of
functions not having a wide contract would be vector::front() and
vector::operator[](size_type) .
Narrow Contracts
A narrow contract is a contract which is not wide. Narrow contracts for a
functions or operations result in undefined behavior when called in a
manner that violates the documented contract. Such a contract
specifies at least one precondition involving its arguments, object
state, or some external global state, such as the initialization of a
static object. Good examples of standard functions with narrow
contracts are vector::front() and vector::operator[](size_type)
.
and recommends:
Each library function having a wide contract, that the LWG agree
cannot throw, should be marked as unconditionally noexcept.
and implies that functions with narrow contracts should not be noexcept
, which is backed up by LWG issue 2337 which says:
[...]These design considerations override our general policy against noexcept for narrow-contract functions. [...]
So if we want to be conservative and follow standard library practice then it would seem since operator[]
does not have a wide contract it should not be marked noexcept
.