Почему 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);
  1. Позволять

    - 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.

[...]

  1. Эффекты: оценивает 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, тогда библиотека с радостью сделает это, не допустив, чтобы это работало.

Другие вопросы по тегам