Уже было изрядное количество обреченных на std::string_view. Это опасно по своей сути, и люди правы, когда делают паузу. Я думаю, что правильно рассматривать его как инструмент для оптимизации, и это связано с существенной и вечной оговоркой, которую Дональд Кнут сформулировал так:

Настоящая проблема заключается в том, что программисты тратят слишком много времени на беспокойство об эффективности не в том месте и не в то время; преждевременная оптимизация — корень всех зол (или, по крайней мере, большей их части) в программировании.

Поэтому я думаю, что лучший урок здесь — не используйте std::string_view, если он вам не нужен, и даже тогда помните о потенциальных ловушках. К сожалению, как я выяснил, самый очевидный вариант использования по-прежнему вероломный. Рассмотрим следующую функцию, в которой я получаю очень большую строку — порядка 1 миллиона символов или более — содержащую большую подстроку интересующего меня содержимого.

using std::string;
using std::string_view;
/* what should this return? */ get_data() {
    string document = get_really_long_string();
    string_view relevant_content = get_relevant_chunk(content);
    return /* ? */;
}

Таким образом, очевидно неправильный ответ заключается в том, что он должен возвращать std::string_view. Очевидно правильный ответ - просто создать новую строку из строкового представления и вернуть ее. В моем случае подстрока представляла собой почти всю очень длинную строку, я просто вырезал заголовок и пустые строки в конце. Это означает, что я, по сути, выполнял глубокую копию, и это было заметно при использовании программы. Таким образом, хотя возврат std::string и действителен, он не соответствует моим требованиям. Итак, мы подошли к неочевидно неправильному решению, которое я реализовал первым.

using std::string;
using std::string_view;
std::tuple<string, string_view> get_data() {
    string document = get_really_long_string();
    string_view relevant_content = get_relevant_chunk(content);
    return {document, relevant_content};
}

На самом деле, когда я скомпилировал это на clang++, это сработало как шарм! Почему бы и нет? Строка порядка мегабайта почти наверняка находится в куче — в стандарте ничего не указано — поэтому string_view — это просто указатель на фиксированное место в куче, а перемещение базового std::string — это просто перемещение его дескриптора по стеку. Однако, скомпилированный под g++, я сразу же получил segfaults.

После обсуждения с некоторыми проницательными людьми на #include ‹C++› я указал на часть стандарта C++, которая явно определяет, когда указатели на данные std::string могут быть признаны недействительными. Для нас актуальна следующая часть:

(4) Ссылки, указатели и итераторы, относящиеся к элементам последовательности basic_string, могут стать недействительными в результате следующих применений этого объекта basic_string:

(4.1) Передача в качестве аргумента любой стандартной библиотечной функции, принимающей в качестве аргумента ссылку на неконстантную базовую_строку.

(4.2) Вызов неконстантных функций-членов, кроме operator[], at, data, front, back, begin, rbegin, end и rend.

Конструктор перемещения, конечно, не является const, и, таким образом, может сделать недействительными std::string_views в std::string. Это делает приведенный выше код непереносимым и, таким образом, убивает его полезность для меня. Итак, я получил раздражающий, но правильный и переносимый неочевидный правильный ответ:

using /* the good stuff */;
tuple<string, ptrdiff_t, size_t> get_data() {
    string document = get_really_long_string();
    string_view relevant_content = get_relevant_chunk(content);
    ptrdiff_t offset = relevant_content.data() - document.c_string();
    size_t length = relevant_content.size();
    return {document, offset, length};
}

Который звонящий затем может рекомбинировать в std::string_view для своих гнусных целей. В моем реальном коде я только что изменил функцию get_relevant_chunk, чтобы она возвращала size_t lpos вместо смещения указателя, но идея та же.