Q:

Durability of string's reverse iterators for self appending

The question:
Suppose I have a string, and I want to generate a new string that has the original string and its reverse concatenated.
Is the following guaranteed to work?
auto pq = [](std::string &s){
    s.reserve(2*s.size());
    s.append(s.rbegin(), s.rend());
};

I see that reserve is supposed to set capacity appropriately. But, does applying append against the reverse iterators cause invalidation of those iterators?
Additional background:
My copy for C++.11 (which has the same language as the C++.17 draft), says in §[string.capacity]

void reserve(size_type res_arg=0);

The member function reserve() is a directive that informs a basic_string object of a planned change
  in size, so that it can manage the storage allocation accordingly.
Effects: After reserve(), capacity() is greater or equal to the argument of reserve. [ Note: Calling
  reserve() with a res_arg argument less than capacity() is in effect a non-binding shrink request.
  A call with res_arg <= size() is in effect a non-binding shrink-to-fit request. — end note ]
Throws: length_error if res_arg > max_size().227

227) reserve() uses allocator_traits::allocate() which may throw an appropriate exception.

While, §[string.append] says

basic_string&
append(const charT* s, size_type n);

Requires: s points to an array of at least n elements of charT.
Throws: length_error if size() + n > max_size().
Effects: The function replaces the string controlled by *this with a string of length size() + n
  whose first size() elements are a copy of the original string controlled by *this and whose remaining
  elements are a copy of the initial n elements of s.
Returns: *this.

A:

This is not the overload of std::string::append you actually call. The one you call is template<class InputIterator> basic_string& append(InputIterator first, InputIterator last);. There, the standard says ([string.append]/21) it is equivalent to constructing a new string before appending:

Effects: Equivalent to append(basic_­string(first, last, get_­allocator())).

Note that here, the constructor call basic_­string(first, last, get_­allocator()) creates a temporary string before the call to a different overload of append, so whatever re-allocation happens in the other append is irrelevant. That means that, even without calling reserve first, this should be safe.
Note that there is no guarantee that string is implemented exactly this way; the standard says "Equivalent to", not "Implemented as". The implementation can do anything which has the same results, but in this case "the same results" means that it still needs to work with iterators derived from the same string this is called on.

