Разумно ли использовать std :: basic_string <t>в качестве непрерывного буфера при таргетинге на C ++ 03? - PullRequest
31 голосов
/ 13 февраля 2010

Я знаю, что в C ++ 03 технически шаблон std::basic_string не обязательно должен иметь непрерывную память. Тем не менее, мне любопытно, сколько существует реализаций для современных компиляторов, которые фактически используют эту свободу. Например, если кто-то хочет использовать basic_string для получения результатов некоторого C API (как в примере ниже), кажется глупым выделять вектор, чтобы сразу превратить его в строку.

Пример:

DWORD valueLength = 0;
DWORD type;
LONG errorCheck = RegQueryValueExW(
        hWin32,
        value.c_str(),
        NULL,
        &type,
        NULL,
        &valueLength);

if (errorCheck != ERROR_SUCCESS)
    WindowsApiException::Throw(errorCheck);
else if (valueLength == 0)
    return std::wstring();

std::wstring buffer;
do
{
    buffer.resize(valueLength/sizeof(wchar_t));
    errorCheck = RegQueryValueExW(
            hWin32,
            value.c_str(),
            NULL,
            &type,
            &buffer[0],
            &valueLength);
} while (errorCheck == ERROR_MORE_DATA);

if (errorCheck != ERROR_SUCCESS)
    WindowsApiException::Throw(errorCheck);

return buffer;

Я знаю, что подобный код может немного уменьшить переносимость, поскольку подразумевает, что std::wstring является смежным, но мне интересно, насколько непереносимым является этот код. Другими словами, как компиляторы могут на самом деле воспользоваться свободой, которую дает несмежная память?


РЕДАКТИРОВАТЬ: Я обновил этот вопрос, чтобы упомянуть C ++ 03. Читатели должны заметить, что при нацеливании на C ++ 11 стандарт теперь требует, чтобы basic_string был смежным, поэтому вышеупомянутый вопрос не является проблемой при нацеливании на этот стандарт.

Ответы [ 5 ]

24 голосов
/ 13 февраля 2010

Я бы посчитал вполне безопасным предположить, что std :: string распределяет свое хранилище непрерывно.

В настоящее время все известные реализации std::string выделяют пространство непрерывно.

Кроме того, текущий черновик C ++ 0x ( N3000 ) [Правка: Предупреждение, прямая ссылка на большой PDF] требует, чтобы пространство выделялось непрерывно (§21.4.1 / 5):

Символьные объекты в объекте basic_string должны храниться непрерывно.То есть для любого объекта s из basic_string идентификатор & * (s.begin () + n) == & * s.begin () + n должен выполняться для всех значений n, таких что 0 <= n <s.size(). </p>

Таким образом, шансы текущей или будущей реализации std::string с использованием несмежного хранилища по существу равны нулю.

13 голосов
/ 13 февраля 2010

Некоторое время назад возник вопрос о возможности записи в хранилище для std::string, как если бы это был массив символов, и он зависел от того, является ли содержимое std::string смежным:

В моем ответе указывалось, что согласно паре хорошо изученных источников (Херб Саттер и Мэтт Остерн), действующий стандарт C ++ требует std::string для хранения данных, смежных при определенных условиях (если вы вызываете str[0], предполагая, что * std::string), и этот факт в значительной степени побуждает руку к любой реализации.

По сути, если вы объединяете обещания, сделанные string::data() и string::operator[](), вы заключаете, что &str[0] должен возвращать непрерывный буфер. Поэтому Аустерн предлагает, чтобы комитет просто сделал это явным, и, очевидно, именно это произойдет в стандарте 0x (или они сейчас называют это стандартом 1x?).

Таким образом, строго говоря, реализация не должна реализовывать std::string с использованием непрерывного хранилища, но она должна делать это в значительной степени по требованию. И ваш пример кода делает это, передавая &buffer[0].

Ссылки

0 голосов
/ 13 февраля 2010

Редактировать: Вы хотите позвонить &buffer[0], , а не buffer.data(), потому что [] возвращает ссылку не const, а делает уведомить объект о том, что его содержимое может неожиданно измениться.


Было бы чище сделать buffer.data(), но вам следует меньше беспокоиться о непрерывной памяти, чем о памяти, разделяемой между структурами. string Реализации могут и действительно ожидать, когда будут сообщены, когда объект изменяется. string::data специально требует, чтобы программа не изменяла возвращенный внутренний буфер.

ОЧЕНЬ велика вероятность того, что в какой-то реализации будет создан один буфер для всех неинициализированных строк, кроме того, если длина установлена ​​в 10 или что-то еще.

Используйте vector или даже массив с new[] / delete[]. Если вы действительно не можете скопировать буфер, юридически инициализируйте строку во что-то уникальное перед ее изменением.

0 голосов
/ 13 февраля 2010

Конечно, выделять вектор здесь глупо. Использование std :: wstring здесь также нецелесообразно. Лучше использовать массив char для вызова winapi. построить wstring при возвращении значения.

0 голосов
/ 13 февраля 2010

Результат не определен, и я бы этого не делал. Стоимость чтения в вектор с последующим преобразованием в строку в современных кучах c ++ тривиальна. VS риск того, что ваш код умрет в Windows 9

также, разве не нужен const_cast для & buffer [0]?

...