2015-10-08 2 views
5

У меня есть функция template<typename T>, которая принимает const vector<T>&. В указанной функции у меня есть векторы cbegin(), cend(), size() и operator[]. Насколько я понимаю, оба string и vector используют непрерывное пространство, поэтому мне было интересно, могу ли я повторно использовать функцию для обоих типов данных в элегантной манере.Интерпретировать std :: string как std :: vector char_type?

Может ли std::string быть переинтерпретирован как std::vector (соответствующего) символа char_type? Если да, то каковы были бы ограничения?

+0

Это то, для чего были созданы итераторы. –

+0

Вы задумывались над тем, чтобы сделать шаблон шаблоном? – mamahuhu

+0

Опираясь на поведение капюшона класса, а не опубликованный интерфейс, может быть опасным. Какая потребность в этом? – Greg

ответ

13

Если вы делаете свой шаблон только для типа const T& и используете begin(), end() и т. Д., Функции, которые передают как векторный, так и строковый, тогда ваш код будет работать с обоими типами.

+0

Есть ли какая-либо реализация, в которой ** сгенерированный ** код является общим (а не только ** исходным ** кодом)? – 6502

+0

Что вы подразумеваете под сгенерированным кодом? – SergeyA

+0

@ 6502: Сгенерированный код не будет использоваться, если только авторы стандартных библиотек не столкнутся с некоторыми чрезвычайными проблемами, чтобы это произошло. Но на самом деле, почему вас это волнует? При оптимизации операции итератора и оператора [] скомпилируются только по нескольким машинным инструкциям. Это неважно. –

6

Нет гарантий, что планировка string и vector будет такой же. Они теоретически могут быть, но они, вероятно, не имеют какой-либо общей реализации. Поэтому вы не можете сделать это безопасно. См. Ответ Зана для лучшего решения.

Поясню: Если я стандартную библиотеку реализатором и решили реализовать станд :: строка, как так ....

template ... 
class basic_string { 
public: 
    ... 
private: 
    CharT* mData; 
    size_t mSize; 
}; 

и решили реализовать зЬй :: вектор, как так ...

template ... 
class vector { 
public: 
    ... 
private: 
    T* mEnd; 
    T* mBegin; 
}; 

Когда reinterpret_cast<string*>(&myVector) вы завершаете интерпретации указателя на конец данных, как указатель на начало данных, и указатель на начало данных с размером данных. Если отступы между членами разные, или есть дополнительные члены, он может стать еще более странным и более сломанным, чем это.

Так что да, для того, чтобы это было возможно, им нужно было хранить смежные данные, но им также нужно совсем другое, чтобы быть одинаковыми между реализациями, которые он должен работать.

+0

Оба 'string' и' vector' используют непрерывный массив для хранения данных. Это означает, что может использоваться нетемпликационная функция, использующая только адреса памяти. –

+0

@AndreyNasonov Вы ошибаетесь. Обновленный ответ, чтобы объяснить это. Пожалуйста, не занимитесь, прежде чем вы поймете :( – David

+0

Я не говорю о расположении полей и конкретной реализации. Я говорю о представлении данных.И 'string', и' vector' предоставляют функцию 'data()', указывающую на первый элемент. Гарантируется, что это непрерывный фрагмент памяти. –

7

Перейти STL-способом и использовать итераторы. Примите итератор, чтобы начать, и итератор закончится. Он будет работать со всеми возможными контейнерами, включая неконтейнеры, такие как потоки.

1

Вы не можете напрямую ввести тип std :: vector в std :: string или наоборот. Но использование итераторов, предоставляемых контейнерами STL, позволяет вам одинаково перебирать вектор и строку. И если ваша функция требует произвольного доступа к рассматриваемому контейнеру, то либо будет работать.

std::vector<char> str1 {'a', 'b', 'c'}; 
std::string str2 = "abc"; 

template<typename Iterator> 
void iterator_function(Iterator begin, Iterator end) 
{ 
    for(Iterator it = begin; it != end; ++it) 
    { 
    std::cout << *it << std::endl; 
    } 
} 

iterator_function(str1.begin(), str1.end()); 
iterator_function(str2.begin(), str2.end()); 

Оба этих последних двух вызова функций будут печатать одно и то же.

Теперь, если вы хотите написать общую версию, которая анализировала только символы, хранящиеся только в строке или в векторе, вы могли бы написать что-то, что итератировало внутренний массив.

void array_function(const char * array, unsigned length) 
{ 
    for(unsigned i = 0; i < length; ++i) 
    { 
    std::cout << array[i] << std::endl; 
    } 
} 

Обе функции будут делать то же самое в следующих сценариях.

std::vector<char> str1 {'a', 'b', 'c'}; 
std::string str2 = "abc"; 

iterator_function(str1.begin(), str1.end()); 
iterator_function(str2.begin(), str2.end()); 
array_function(str1.data(), str1.size()); 
array_function(str2.data(), str2.size()); 

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

2

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

void myfunc(const CType *p, int size) { 
    ... 
} 

, чтобы понять, что вы предположить, что они должен быть смежным в памяти.

Тогда, например, чтобы передать содержимое вектора кода просто

myfunc(&myvect[0], myvect.size()); 

и для строки

myfunc(mystr.data(), mystr.size()); 

или

myfunc(buffer, n); 

для массива.

+0

Все говорят итераторы, вы говорите указатели :) – SergeyA

+1

Этот подход имеет преимущество: он не использует шаблоны. Но, пожалуйста, измените 'int' на' size_t'. –

+0

@SergeyA: Написание шаблона (в реализациях, которые я знаю) разделит ** исходный код **, создавая, однако, четкий код для разных типов. – 6502

5

std::experimental::array_view<const char>n4512 представляет собой непрерывный буфер символов.

Письмо your own is not hard, и оно решает эту проблему и (по моему опыту) еще много.

И строка, и вектор совместимы с видом массива.

Это позволяет перемещать реализацию в .cpp файл (а не подвергать его воздействию), дает такую ​​же производительность, как это делать с std::vector<T> const& и, возможно, ту же реализацию, позволяет избежать дублирования кода и использует легкий вес смежный тип буфера стирания (который полон вкусных ключевых слов).

0

Способ, которым ваш вопрос задан в данный момент, немного запутан. Если вы хотите спросить: «Безопасно ли использовать тип std::vector для типа std::string или наоборот, если вектор содержит значения символов соответствующего типа?», Ответ: никоим образом, даже не думайте о Это! Если вы спрашиваете: «Могу ли я получить доступ к непрерывной памяти непустых последовательностей типа char, если они имеют тип std::vector или std::string?» то ответ: да, вы можете (с функцией члена data()).

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