Почему C++23 string::resize_and_overwrite вызывает операцию как rvalue?
Для повышения производительности записи данных в C++23 специально введен resize_and_overwrite()
для
std::string
. В [string.capacity] стандарт описывает это следующим образом:
template<class Operation> constexpr void resize_and_overwrite(size_type n, Operation op);
Позволять
-
o = size()
перед звонком вresize_and_overwrite
.-
k
бытьmin(o, n)
.- быть
charT*
, такие, что диапазон [,] действителен иthis->compare(0, k, p, k) == 0
являетсяtrue
перед звонком. Значения в диапазоне [p + k
,] может быть неопределенным [basic.indet] .- быть выражением
std::move(op)(p, n)
.-
r = OP
.[...]
- Эффекты: оценивает
OP
, заменяет содержимое*this
с участием [,p + r
) и аннулирует все указатели и ссылки на диапазон [p
,p + n
].
Но я узнал, что эта функция будет использовать
std::move
преобразовать в rvalue перед его вызовом, что означает, что мы не можем передать вызываемый объект, у которого только lvalue перегружено
operator()
(демо):
#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
}
Такое поведение кажется немного странным, но, поскольку это изменение было внесено в последнее издание статьи, оно явно сделано намеренно.
Итак, какие соображения стоят за этим? Есть ли какая-то польза от мандата, который
op
должен вызываться как rvalue?
1 ответ
op
вызывается только один раз, прежде чем он будет уничтожен, поэтому вызов его как rvalue разрешает любое
&&
перегрузить его, чтобы повторно использовать любые ресурсы, которые он может содержать.
Вызываемый объект является морально xvalue - это «истекающий» , потому что он будет уничтожен сразу после вызова. Если вы специально разработали свой вызываемый объект для поддержки вызова только как lvalues, тогда библиотека с радостью сделает это, не допустив, чтобы это работало.