2015-11-17 8 views
2

В C++ вы можете инициализировать объект std::string из char * и const char *, и это подразумевает, что строка будет заканчиваться сначала NUL символом, найденным после указателя.Почему бы не разрешить инициализацию std :: string из массива символов?

В строковых литералах C++, однако, массивы и конструктор шаблонов могут использоваться для получения правильного размера, даже если строковый литерал содержит встроенные NUL s. Смотри, например, следующую реализацию игрушек:

#include <stdio.h> 
#include <string.h> 
#include <vector> 
#include <string> 

struct String { 
    std::vector<char> data; 
    int size() const { return data.size(); } 

    template<typename T> String(const T s); 

    // Hack: the array will also possibly contain an ending NUL 
    // we don't want... 
    template<int N> String(const char (&s)[N]) 
     : data(s, s+N-(N>0 && s[N-1]=='\0')) {} 

    // The non-const array removed as probably a lot of code 
    // builds strings into char arrays and the convert them 
    // implicitly to string objects. 
    //template<int N> String(char (&s)[N]) : data(s, s+N) {} 
}; 

// (one tricky part is that you cannot just declare a constructor 
// accepting a `const char *` because that would win over the template 
// constructor... here I made that constructor a template too but I'm 
// no template programming guru and may be there are better ways). 
template<> String::String(const char *s) : data(s, s+strlen(s)) {} 

int main(int argc, const char *argv[]) { 
    String s1 = "Hello\0world\n"; 
    printf("Length s1 -> %i\n", s1.size()); 
    const char *s2 = "Hello\0world\n"; 
    printf("Length s2 -> %i\n", String(s2).size()); 
    std::string s3 = "Hello\0world\n"; 
    printf("std::string size = %i\n", int(s3.size())); 
    return 0; 
} 

Есть ли какие-либо конкретные технические причины, по которым этот подход не был рассмотрен для стандарта и вместо строкового литерала со встроенными NUL с заканчивается усекается, когда используется для инициализации std::string объект?

+3

Для начала, после 'char a [100] =" foo "; std :: string s = a; ',' s.length() 'вероятно, не должно быть 100. –

+0

" строковый литерал со встроенными NUL заканчивается тем, что усекается при инициализации объекта std :: string "- только если это произойдет. –

+1

Вероятно, потому что наличие строк со встроенными нулевыми символами является исключительным случаем, с которым можно было бы обращаться другими способами. –

ответ

2

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

Это проще, если есть выделенный берет-массив ссылок шаблона конструктора, но, как вы заметили

  • такого шаблона, с только параметром массива, будет считаться хуже матчем, чем строитель, просто сделавший char const*, и

  • было бы неясно, должно ли быть включено окончательное значение nullvalue или нет.

Первая точка означает, что физический интерфейс код будет один шаблонного конструктора, где только документация (а не подсказка вашего редактора, например) будет рассказать всю историю о том, что она acccepted или нет. Одним из исправлений является введение дополнительного аргумента аргумента-аргумента. Это уменьшает удобство.

Второй момент - возможность введения ошибок. Наиболее частое использование конструктора, несомненно, будет обычным строковым литералом. Затем, время от времени, он будет использоваться для литералов и/или массивов со встроенными нулевыми байтами, но с любопытством с последним персонажем отрубается.

Вместо можно просто назвать первое значение,

char const data[] = "*.com\0*.exe\0*.bat\0*.cmd\0"; 
string s(data, data + sizeof(data)); // Including 2 nulls at end. 

Все, что сказал, когда я определил свои собственные строки классов Я включил конструктор принимает массив-аргументов, но очень разные разум, чем удобство. А именно, что в случае литерала строковый объект может просто удерживать этот указатель без копирования, что обеспечивает не только эффективность, но и безопасность (правильность), например. исключения. И массив из const char является самым ясным признаком буквального, что у нас есть в C++ 11 и более поздних версиях.

Однако std::string не может этого сделать: он не предназначен для этого.


Если это часто делается, то можно было бы определить функцию:

using Size = ptrdiff_t; 

template< Size n > 
auto string_from_data(char const (&data)[n]) 
    -> std::string 
{ return std::string(data, data + n); } 

Тогда можно написать только

string const s = string_from_data("*.com\0*.exe\0*.bat\0*.cmd\0"); 

Отказ от ответственности: ни один из кодов не прикоснулся или видели компилятор.


[Я пропустил это на первом письменном виде, но напомнили Hurkyl's answer. ! Теперь заголовок для кофе]

C++ 14 строки типа буквенных отбивных от окончательного \0, поэтому с такими буквальным выше должен включать в себя, что прекращение nullvalue явно:

string const s = "*.com\0*.exe\0*.bat\0*.cmd\0\0"s; 

Кроме того, Строковые литералы типа C++ 14, как представляется, обеспечивают поиск удобства.

+0

Я пропустил слона в комнате. Конечно, никто не полюбит окончание 'NUL' в строке. – 6502

3

C++ 14 вводит суффикс для строковых литералов, чтобы сделать их объектами std::string, поэтому основной вариант использования больше не имеет значения.

#include <iostream> 
#include <string> 
using namespace std; 
using namespace std::literals; 

int main() { 
    string foo = "Hello\0world\n"; 
    string bar = "Hello\0world\n"s; 
    cout << foo.size() << " " << bar.size() << endl; // 5 12 
    cout << foo << endl; // Hello 
    cout << bar << endl; // Helloworld 
    return 0; 
} 
+0

Приобретено для запоминания того, что мне удалось забыть. Благодарю. –

 Смежные вопросы

  • Нет связанных вопросов^_^