Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++23 string::resize_and_overwrite invoke operation as an rvalue?

Tags:

c++

string

c++23

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);
  1. Let

    o = size() before the call to resize_and_overwrite.

    k be min(o, n).

    p be a charT*, such that the range [p, p + n] is valid and this->compare(0, k, p, k) == 0 is true before the call. The values in the range [p + k, p + n] may be indeterminate [basic.indet].

    OP be the expresion std::move(op)(p, n).

    r = OP.

[...]

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

like image 927
康桓瑋 Avatar asked Oct 18 '21 09:10

康桓瑋


People also ask

Why use C++23 for string handling?

One of the main reasons to use C++ is its high performance. An area where we often use the language in a non-efficient way is string handling. C++23 will bring us another string member function that will help us to handle strings in a more performant way.

How to call contains() with a string in C++23?

With C++23, std::string and std::string_view will have similar capabilities. You can call contains () with either a string or a character and it will return true or false depending on whether the queried string or string_view contains the input parameter.

How does initializing and manipulating strings affect performance sensitive code?

Performance sensitive code is impacted by the cost of initializing and manipulating strings. When writing data into a basic_string, a programmer is faced with an unhappy choice: Pay for extra initialization — resize, which zero initializes, followed by copy. Pay for extra copies — Populate a temporary buffer, copy it to the string.

Should resize_and_overwrite have exception guarantees?

The authors further recommend UB if op were to throw. resize_and_overwrite is motivated by measured performance improvements that are small individually but significant at scale. Providing exception guarantees would incur overhead that is in tension with the original motivation.


1 Answers

op is only called once before it is destroyed, so calling it as an rvalue permits any && overload on it to reuse any resources it might hold.

The callable object is morally an xvalue - it is "expiring" because it is destroyed immediately after the call. If you specifically designed your callable to only support calling as lvalues, then the library is happy to oblige by preventing this from working.

like image 124
T.C. Avatar answered Oct 17 '22 21:10

T.C.