In order to improve the performance of writing data into std::string
, C++23 specially introduced resize_and_overwrite()
for std::string
. In [string.capacity], the standard describes it as follows:
template<class Operation> constexpr void resize_and_overwrite(size_type n, Operation op);
Let
—
o = size()
before the call toresize_and_overwrite
.—
k
bemin(o, n)
.—
p
be acharT*
, such that the range [p
,p + n
] is valid andthis->compare(0, k, p, k) == 0
istrue
before the call. The values in the range [p + k
,p + n
] may be indeterminate [basic.indet].—
OP
be the expresionstd::move(op)(p, n)
.—
r = OP
.[...]
- Effects: Evaluates
OP
, replaces the contents of*this
with [p
,p + r
), and invalidates all pointers and references to the range [p
,p + n
].
But I found out that this function will use std::move
to convert op
into an rvalue before invoking it, which means that we cannot pass in a callable that only has lvalue overloaded operator()
(demo):
#include <string>
int main() {
struct Op {
int operator()(char*, int) &;
int operator()(char*, int) && = delete;
} op;
std::string s;
s.resize_and_overwrite(42, op); // ill-formed
}
This behavior seems a bit strange, but since this change was made in the last edition of the paper, it is obviously intentional.
So, what are the considerations behind this? Is there any benefit in the mandate that op
must be invoked as an rvalue?
op
(such as a one-time-consumable functor). It is relatively unusual to delete the rvalue-qualified operator. If you cannot take advantage of rvalue*this
, you usually just fall back to unqualified. – Unmixed