Инициализировать std::string с помощью указателя NULL char - программирование
Подтвердить что ты не робот

Инициализировать std::string с помощью указателя NULL char

Инициализация std::string от указателя NULL char - это поведение undefined, я считаю. Итак, вот альтернативные варианты конструктора, где mStdString является переменной-членом типа std::string:

void MyClass::MyClass(const char *cstr) :
    mStdString( cstr ? cstr : "")
{}

void MyClass::MyClass(const char *cstr) :
    mStdString(cstr ? std::string(cstr) : std::string())
{}

void MyClass::MyClass(const char *cstr)
{
    if (cstr) mStdString = cstr;
    // else keep default-constructed mStdString
}

Изменить, объявление конструктора внутри class MyClass:

MyClass(const char *cstr = NULL);

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

Я ищу ответ с объяснением, почему правильный путь или ответ с ссылкой ссылки (это также применяется, если ответ "не имеет значения" ), а не только личные мнения (но если вы должны, по крайней мере, сделать это просто комментарием).

4b9b3361

Ответ 1

Последний глупый, потому что он не использует инициализацию, когда это возможно.

Первые два полностью идентичны семантически (думайте о функции члена c_str()), поэтому предпочитайте первую версию, потому что она самая прямая и идиоматическая и проще всего читать.

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


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

: cstr ? cstr : std::string()

Как чтение, так и построение по умолчанию.


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


Для хихиканья я запустил обе версии через Clang 3.3, с -O3, на x86_64, для struct foo;, как ваш, и функцию foo bar(char const * p) { return p; }:

Конструктор по умолчанию (std::string()):

    .cfi_offset r14, -16
    mov     R14, RSI
    mov     RBX, RDI
    test    R14, R14
    je      .LBB0_2
    mov     RDI, R14
    call    strlen
    mov     RDI, RBX
    mov     RSI, R14
    mov     RDX, RAX
    call    _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
    jmp     .LBB0_3
.LBB0_2:
    xorps   XMM0, XMM0
    movups  XMMWORD PTR [RBX], XMM0
    mov     QWORD PTR [RBX + 16], 0
.LBB0_3:
    mov     RAX, RBX
    add     RSP, 8
    pop     RBX
    pop     R14
    ret

Конструктор пустой строки (""):

    .cfi_offset r14, -16
    mov     R14, RDI
    mov     EBX, .L.str
    test    RSI, RSI
    cmovne  RBX, RSI
    mov     RDI, RBX
    call    strlen
    mov     RDI, R14
    mov     RSI, RBX
    mov     RDX, RAX
    call    _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm
    mov     RAX, R14
    add     RSP, 8
    pop     RBX
    pop     R14
    ret

.L.str:
    .zero    1
    .size    .L.str, 1

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

Ответ 2

Предполагая, что вы довольны cstr == NULL, давая пустой mStdString, я думаю, что первый из них, вероятно, лучший.

Если ничего другого, третий параметр, который вы предоставляете, не работает, если mStdString - const. Средний вариант выигрывает от "семантики перемещения" под С++ 11, но менее явно оптимален или разумен.

Итак, мой голос идет с первым вариантом.

Ответ 3

Прежде всего, вы правы, от http://www.cplusplus.com/reference/string/string/string/:

Если s является нулевым указателем, если n == npos или если диапазон, указанный в [first, last], недействителен, он вызывает поведение undefined.

Кроме того, это зависит от того, что для вас означает указатель NULL. Я предполагаю, что это то же самое, что пустая строка для вас.

Я бы пошел с первым, потому что он тот, который я читал лучше всего. Первое решение и второе - одно и то же. Третий не работает, если ваша строка const.

Ответ 4

В то время как это не может быть ДЕЙСТВИТЕЛЬНО ответом (особенно, поскольку вы сформулировали вопрос), но он слишком длинный, чтобы соответствовать как комментарий, и имеет код в нем, который не имеет комментариев в комментариях. Я полностью ожидаю, что я заберусь вниз и должен удалить этот пост, но я вынужден сказать что-то.

ПОЧЕМУ была бы инициализация char * NULL - и если да, не могли бы вы подтолкнуть ее к вызывающей стороне, чтобы иметь представление об этой ситуации - например, передать пустую строку или "unknown" или "(null)" как подходящее.

Другими словами, что-то вроде этого:

void MyClass::MyClass(const char *cstr) 
{ 
    assert(cstr != NULL);   // or "throw cstr_must_not_be_null;" or some such. 
    mStdString = cstr;
}

(Вероятно, есть какой-то умный способ сделать это в списке инициализаторов, но я не могу понять, как правильно это сделать).

I для одного не увлекается NULL как входным параметром строки любым другим способом, чем "Это действительно не существует" - и если это то, что вы на самом деле пытаетесь воспроизвести, тогда вы должны иметь boolean сказать "не существует" или указатель на std::string, который может быть NULL, если строка не присутствует.