13

This is overload (4) here

In the "Exceptions" section, overloads 2,3,5,6 (which have pos1 and/or pos2 parameters) are named as throwing std::out_of_range.

Overload (4) does not have "pos" parameters, but it's not marked noexcept.

Is it up to the implementation whether it throws or not?

In GCC 7's libstdc++, it calls char_traits<char>::length and char_traits<char>::compare. These don't seem to be able to throw, but aren't marked noexcept.

Hatted Rooster
  • 35,759
  • 6
  • 62
  • 122
Bulletmagnet
  • 5,665
  • 2
  • 26
  • 56
  • 2
    Honestly not sure why this question is getting so many upvotes.. `Clang` has it as: `int compare(const value_type* __s) const _NOEXCEPT;` So does GCC: `int compare(const _CharT* __s) const _GLIBCXX_NOEXCEPT;` https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/basic_string.h#L2988 – Brandon Jul 10 '19 at 13:56
  • Looks like it's up to implementation, but logically it seems like it should be `noexcept`. – anonmess Jul 10 '19 at 13:58
  • 1
    I guess we need a language lawyer to get the real answer...but I would assume, that the standard does not require `std::string` to store the string as a `CharT *`, in order to allow small string optimization. However, this also means that we might have to do some conversion for the actual `compare` to happen. This conversion might have to allocate memory, which could be exhausted and therefore possibly throw. Thus, the standard cannot enforce a `noexcept` from implementations, although they still might be `noexcept` in practice. – jan.sende Jul 10 '19 at 14:21

1 Answers1

2

Except for destructors, swap functions, move constructors and move assignment operators, the standard marks a function noexcept only if it has a wide contract, i.e., it has no preconditions. This overload requires the argument to be a null-terminated string, so the standard does not mark it as noexcept.

The rational is specified in N3248:

Functions marked noexcept are difficult to test

When a function is marked with noexcept it becomes impossible to flag test failures, notably in test drivers, by throwing an exception. A common example would be code that validates preconditions on entry to a function:

T& std::vector<T>::front() noexcept {
 assert(!this->empty());
 return *this->data();
}

When validating such defensive checks from a test driver, a reasonable approach is to register an assert-handler that throws a well-defined precondition-violated exception, which the test driver catches to ensure that the appropriate asserts are indeed in place.

...

Now we might argue that calling the function out-of-contract, when the vector is empty, is undefined behavior so we should not expect any guarantees. The problem is that undefined behavior is being specified by the library; to the compiler, this code is perfectly well defined and, if assert throws an exception, the program must terminate in a well-specified manner, thwarting the test driver.

Note that the issue here is not that we are using assertions to find bugs in our own library implementations, but rather in user code that incorrectly calls into our library. If we remove the ability to test these defensive assertions, we may get them wrong, and thus put our users at risk for committing far more serious errors than propagating an unexpected exception.


By the way, due to [res.on.exception.handling]/5:

An implementation may strengthen the exception specification for a non-virtual function by adding a non-throwing exception specification.

... libstdc++ and libc++ are free to mark this overload noexcept.

xskxzr
  • 12,442
  • 12
  • 37
  • 77