Подтвердить что ты не робот

Строка С++ недостаточно оптимизирована для строковых литералов

В моем проекте на С++ я за один шаг до замены всего char* на std::string, но я нахожу один случай, когда std::string терпит неудачу.

Представьте, что у меня есть две функции:

void foo1(const std::string& s)
{
    ...
}

void foo2(const char* s)
{
    ...
}

Если я напишу что-то вроде этого:

const char* SL = "Hello to all!";

foo1(SL); // calls malloc, memcpy, free
foo2(SL);

в foo1 SL будет неявно преобразован в std::string. Это означает, что конструктор std::string будет выделять память, и он скопирует строковый литерал в этот буфер. В foo2, хотя ничего из этого не произойдет.

В большинстве реализаций std::string предполагается супер оптимизироваться (например, копировать по записи), но когда я построю его с помощью const char*, это не так. И мой вопрос в следующем: почему это происходит? Я что-то упускаю? Является ли моя стандартная библиотека недостаточно оптимизированной или по какой-либо причине (что я не знаю) это абсолютно небезопасно?

4b9b3361

Ответ 1

На самом деле, ваши заботы исчезнут (*), если вы изменили литерал:

std::string const SL = "Hello to all!";

Я добавил const для вас.

Теперь вызов foo1 не будет содержать никакого копирования (вообще), а вызов foo2 может быть достигнут за небольшую плату:

foo1(SL);         // by const-reference, exact same cost than a pointer
foo2(SL.c_str()); // simple pointer

Если вы хотите перейти на std::string, не только переключайте интерфейсы функций, также переключайте переменные (и константы).

(*) Исходный ответ предполагал, что SL является глобальной константой, если она является переменной, локальной для функции, тогда ее можно было бы сделать static, если вы действительно хотите избежать ее построения при каждом вызове.

Ответ 2

Проблема заключается в том, что класс std::string не может распознать, является ли указатель const char* глобальным символьным литералом или нет:

const char *a = "Hello World";
const char *b = new char[20];

Указатель char * может быть недействителен в любое время (например, когда он локальная переменная и функция/область заканчивается), таким образом std::string должен стать эксклюзивным владельцем строки. Это может быть достигнуто только при копировании.

В следующем примере показано, почему это необходимо:

std::string getHelloWorld()  {
  char *hello = new char[64];
  strcpy(hello, "Hello World");
  std::string result = (const char *)hello;  // If std::string didn't make a copy, the result could be a garbage
  delete[] hello;
  return result;
}

Ответ 3

std::string не является серебряной пулей. Он предназначен для наилучшей реализации универсальной изменяемой строки, которая владеет своей памятью и которая достаточно дешева для использования с C API. Это обычные сценарии, но они не соответствуют каждому экземпляру использования строки.

Строковые литералы, как вы говорите, не подходят для этого варианта использования. Они используют статически выделенную память, поэтому std::string не может и не должен пытаться завладеть памятью. И эти строки всегда доступны только для чтения, поэтому std::string не позволяет вам изменять их.

std::string создает копию переданных ему строковых данных, а затем внутренне работает с этой копией.

Если вы хотите работать с постоянными строками, чье время жизни обрабатывается в другом месте (в случае строковых литералов оно обрабатывается библиотекой времени выполнения, которая инициализирует и освобождает статические данные), тогда вы можете использовать другое строковое представление. Возможно, просто простой const char*.