Инициализировать std:: string из возможно нулевого указателя char

инициализации std::string С NULL char указатель-это неопределенное поведение, я считаю. Итак, вот альтернативные версии конструктора, где 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
}

Edit, объявление конструктора внутри class MyClass:

MyClass(const char *cstr = NULL);

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

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

4 ответов


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

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


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

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

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


во-первых, во-первых, вы правы, от http://www.cplusplus.com/reference/string/string/string/:

Если S является нулевым указателем, если n == npos или если диапазон,указанный [first, last), недопустим, это вызывает неопределенное поведение.

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

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


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

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

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

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

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

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