Это разумный для использования станд.:: basic_string <t> как непрерывный буфер при предназначении для C++ 03?

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

Пример:

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 будьте непрерывны, таким образом, вышеупомянутый вопрос не, выходят при предназначении для того стандарта.

32
задан Billy ONeal 8 November 2012 в 18:04
поделиться

5 ответов

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

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

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

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

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

25
ответ дан 27 November 2019 в 21:08
поделиться

Короче говоря, он оптимизирован CLR (за вычетом памяти).

Кроме того, сравнение в верхнем регистре более оптимизировано, чем ToLower (), если эта небольшая степень производительности имеет значение.

В ответ на ваш пример существует более быстрый способ :

String.Equals(type.Name, controllerName + "Controller", 
              StringComparison.InvariantCultureIgnoreCase);
-121--2461694-

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

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

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

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

Ссылки:

14
ответ дан 27 November 2019 в 21:08
поделиться

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


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

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

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

0
ответ дан 27 November 2019 в 21:08
поделиться

Проблема заключается в том, что вы пытаетесь создать или обновить поля в объекте User, который еще не существует. Поэтому другие поля не обновляются, поскольку они не связаны ни с какими первичными ключами дочерних полей.

Каждый раз, когда создается экземпляр нового поля модели, необходимо убедиться, что вы сохраняете его, чтобы дочернее поле модели имело идентификатор (первичный ключ) для связи.

Вам нужно нечто более подобное:

def save(self):
    user = User(
        username = self.cleaned_data['username'],
        email = self.cleaned_data['email1'],
        first_name = self.cleaned_data['first_name'],
        last_name = self.cleaned_data['last_name'],
    )
    ## save user so we get an id
    user.save()

    ## make sure we have a user.id
    if user.id:
        ## this doesn't save the password, just updates the working instance
        user.set_password(self.cleaned_data['password1'])
        user.profile = Profile(
            primary_phone = self.cleaned_data['phone'],
        )
        ## save the profile so we get an id
        user.profile.save()

    ## make sure we have a profile.id
    if user.profile.id:
        user.profile.address = Address(
            country = self.cleaned_data['country'],
            province = self.cleaned_data['province'],
            city = self.cleaned_data['city'],
            postal_code = self.cleaned_data['postal_code'],
            street1 = self.cleaned_data['street1'],
            street2 = self.cleaned_data['street2'],
            street3 = self.cleaned_data['street3'],
        )
        ## save the profile address
        user.profile.address.save()

    ## final save to commit password and profile changes
    user.save()
    return user

Эта каскадная save () вещь, которая у вас здесь происходит, просто не чувствует себя правильно. Вы склонны путь слишком много ошибок, если какое-либо из полей не сохранит, вы окажетесь с частично полным экземпляром пользователя и, возможно, окажетесь с дубликатами, если пользователю придется вернуться и повторить попытку. Не весело!

Изменить: Удалено 2-е полугодие из-за неточности.

-121--4378695-

Предположим, что имеются два FSM со состояниями O ( n ). Затем можно создать FSM размера O ( n 2 ), который распознает только симметричное различие их принимающих языков. (Создайте модуль FSM, который имеет состояния, соответствующие паре состояний, по одному из каждого модуля FSM. Затем на каждом шаге обновите каждую часть пары одновременно. Состояние в новом FSM - это состояние принятия iff, ровно одно из пары было состоянием принятия.) Теперь минимизируйте этот FSM и посмотрите, является ли он таким же, как тривиальный одногосударственный FSM, который отвергает все. Минимизация FSM с состояниями m занимает время O ( m регистраций m ), так что в целом можно сделать все во времени O ( n 2 регистраций n ).

@ Агор правильно заявляет, что Sipser является хорошим ориентиром для такого рода вещей. Ключевым пунктом моего ответа является то, что вы можете сделать это за полиномиальное время, даже с небольшой экспонентой.

-121--4245635-

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

-2
ответ дан 27 November 2019 в 21:08
поделиться

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

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

-1
ответ дан 27 November 2019 в 21:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: