Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.56/18: Рейтинг темы: голосов - 18, средняя оценка - 4.56
0 / 0 / 0
Регистрация: 14.02.2016
Сообщений: 8

Конструирование string из stringstream

09.06.2018, 19:38. Показов 4146. Ответов 13

Студворк — интернет-сервис помощи студентам
Я уже довольно долгое время ломаю голову над вот таким вопросом конструирования строки из stringstream

Такой код работает:
C++
1
2
3
4
5
6
7
8
   std::stringstream ss;
   ss.str ("Example string");
   
   std::string::iterator s1 = ss.str().begin();
   std::string::iterator s2 = ss.str().end();
 
   std::string str1(s1, s2);
   std::cout << str1 << "\n";
А такой уже нет:
C++
1
2
3
4
5
   std::stringstream ss;
   ss.str ("Example string");
 
   std::string str1(ss.str().begin(), ss.str().end());
   std::cout << str1 << "\n";
Почему второй вариант не работает? Я не вижу в коде каких-то принципиальных различий.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
09.06.2018, 19:38
Ответы с готовыми решениями:

Можно ли превратить строку string в stringstream?
Сабж.

Странное поведение std::string, полученного из std::stringstream
Есть код #include &lt;iostream&gt; #include &lt;sstream&gt; #include &lt;cstring&gt; int main() { std::stringstream stream; ...

Stringstream C++
Доброго времени суток! Совсем недавно мне понадобились преобразования int to string и наоборот. Заинтересовал метод, используя...

13
 Аватар для igorrr37
2872 / 2019 / 991
Регистрация: 21.12.2010
Сообщений: 3,750
Записей в блоге: 10
10.06.2018, 05:39
Ни один вариант не работает так как str() возвращает копию а не ссылку
2
0 / 0 / 0
Регистрация: 14.02.2016
Сообщений: 8
10.06.2018, 07:49  [ТС]
Нет первый вариант работает и возвращает то, что нужно. Второй просто не работает. Я понимаю, что итераторы не особо корректны, т.к. Они итерируются по уже несуществующему объекту. Но тем не менее все указатели в норме и указывают в одни и те же места, хоть это и приведет к непредвиденным последствиям. Меня интересует именно проблема конструирования строки во втором варианте.

Добавлено через 1 минуту
Если вы не верите в работоспособность, то попробуйте запустить этот код. Я пробовал и локально и в онлайн IDE
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
10.06.2018, 22:38
Цитата Сообщение от egorrich Посмотреть сообщение
Нет первый вариант работает и возвращает то, что нужно.
Не работает. Может в вашей реализации это и проходит, но если где-то не работает то, вообще говоря не работает. Функция член str() являтся геттером/сеттером, то есть возвращает ссылку. С чего вы решили что она должна быть перегружена для пары итераторов?
http://www.cplusplus.com/refer... tream/str/

Добавлено через 31 минуту
Нет не ссылку. В заголовочном sstream есть две перегрузки - одна void сеттер и другая геттер возвращающая копию строки из буффера (по сути str - обёртка над ним) :
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
__string_type
      str() const
      { return _M_stringbuf.str(); }
 
      /**
       *  @brief  Setting a new buffer.
       *  @param  __s  The string to use as a new sequence.
       *
       *  Calls @c rdbuf()->str(s).
      */
      void
      str(const __string_type& __s)
      { _M_stringbuf.str(__s); }
    };
Почему оно компелируется с парой итераторов непонятно. Но ничего хорошего из этого не получается.
1
0 / 0 / 0
Регистрация: 14.02.2016
Сообщений: 8
10.06.2018, 23:47  [ТС]
Вы правы, что это не хорошо. Я с этим не спорю. Компилируется, т.к. у строки есть конструктор принимающий итераторы. В этом какрас и проблема. Оба случая компилируются, в обоих случаях передаются итераторы, которые указывают в одни и те же места. Но вот второй пример кода не работает вовсе. Первый работает в ide срр.sh, например. Локально gcc 6 версии с флагом std=c++14 компилируется и отрабатывает нормально только первый вариант.

Добавлено через 9 минут
http://www.cplusplus.com/refer... ng/string/
Для строки есть конструктор с итераторами.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
11.06.2018, 00:14
Цитата Сообщение от egorrich Посмотреть сообщение
Компилируется, т.к. у строки есть конструктор принимающий итераторы.
Там нет создания объекта string (basic_string<char.... и пр. и бр-р-р). Там есть две перегруженные функции - см. ещё столько раз сколько нужно - выше.
Цитата Сообщение от egorrich Посмотреть сообщение
Первый работает в ide срр.sh, например. Локально gcc 6 версии с флагом std=c++14
Мало ли багов даже у gcc?
У меня более поздний для Win32 + CodeBlock компилирует но выдаёт некоректный результат.
Цитата Сообщение от egorrich Посмотреть сообщение
В этом какрас и проблема.
Какую задачу вам мешает решить данная, как вы говорите, проблема?
0
87 / 87 / 18
Регистрация: 11.06.2018
Сообщений: 302
11.06.2018, 00:39
Лучший ответ Сообщение было отмечено egorrich как решение

Решение

egorrich, господи, да что ж вы такое говорите!
Этот код не работает! Ни первый ни второй. Не работают они по-разному, но не работают оба! Почему? Потому что в первом варианте str() возвращает временный объект. Временный. Объект. Что это означает для вашего кода? Первое - это то, что строка, помещенная в этот временный, никуда не скопированный объект умирает в конце выражения (в строках 4 и 5), а взятие итератора для этой строки дает итератор, указывающий на уже освобожденную память. Второе - объектов у вас два! Вы два раза вызываете str() и два раза это два разных объекта строк. И вы не имеете права использовать эти разные итераторы от разных объектов как итераторы для одной последовательности.

Итак, в первом коде две ошибки. Итераторы, указывающие на мертвую строку и итераторы указывающие на начало и конец разных объектов строк (даже если бы они были живы).
Во втором коде объекты еще живы - т.к. выражение не закончено, но остается вторая ошибка - это разные объекты, пусть и идентичные по содержимому. Begin() от одного объекта, а End() от другого.

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

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

Вот посмотрите сюда: http://rextester.com/NNSYE86106 Это онлайн компилятор и здесь ваш второй нерабочий по вашим словам код работает. Это проявление UB.
А теперь посмотрите сюда: http://rextester.com/JSXM73247. А здесь, наоборот, ваш первый, рабочий по вашим словам код, тем не менее не работает. Это тоже проявление UB.

Не делайте так. Делайте правильно.

Добавлено через 3 минуты
IGPIGP, Пожалуйста, прекратите вводить ТС в заблуждение.
3
0 / 0 / 0
Регистрация: 14.02.2016
Сообщений: 8
11.06.2018, 01:07  [ТС]
При запуске второго варианта вываливается вот такая ошибка:
terminate called after throwing an instance of 'std::length_error'
what(): basic_string::_S_create
Error launching program (Aborted)


Говорит, что длина строка превышает максимальную. Но итераторы указывают на нормальные адреса, так же как и в первом варианте. Но вот этого я и не понимаю, где же проблема. Вроде два одинаковых варианта, но работают по разному. Может все это зависит от конкретного компилятора и не нужно искать смысл в этом?

Добавлено через 10 минут
Большое спасибо за ответ. Вы не подумайте. Я изначально осознавал, что это неопределенное поведение. И нигде такой код не использую. Меня ввели в заблуждение указатели итераторов при отладке. Они указывали на одни и те же адреса, что переменная s1, что адрес, взятый напрямую от ss.str().begin(). Вот я и задался таким вопросом. Ведь все вроде одинаково. Я понял, что рассуждения о UB ни к чему хорошему не приведут и такое поведение может зависет, например, от моего компилятора. Я очень рад, что вы дали мне развернутый ответ. Спасибо.
0
87 / 87 / 18
Регистрация: 11.06.2018
Сообщений: 302
11.06.2018, 01:28
egorrich, теперь, когда вы поняли, что и тот и другой код некорректны, можно попытаться объяснить видимое поведение.
Смотрите:
C++
1
2
3
4
char * p1 = new char[10];
delete[] p1;
char * p2 = new char[10];
delete[] p2;
В некоторых случаях указатели p1 и p2 могут быть равны, т.к. при втором вызове менджер памяти только что выделял точно такой же участок памяти, значит он вполне может вернуть его еще раз. Когда вы наблюдали равенство адресов, это скорее всего было следствием этого явления. Но вы должны понимать, что это поведение ничем не гарантируется. Это особенность реализации конкретного менеджера памяти конкретной библиотеки конкретного компилятора.

В любом случае, по правилам языка С++ вы не имеете права использовать *p1 и *p2 после освобождения их памяти (что происходило в первом примере). И вы также не имеете права использовать p1 и p2 (даже если память еще наша) для представления одной последовательности (например std::string(p1, p2 + 10)) - это происходило в первом и втором примерах.

Второй момент как раз приводит к ошибке length error, которую вы цитировали, т.к. расстояние между p1 и p2 + 10 может значительно превышать размер строки - это же разные последовательности. Если строка проверяет внутри этот инвариант, будет получена ошибка, наподобие процитированной вами.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9005 / 4706 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
11.06.2018, 07:42
Цитата Сообщение от ablex Посмотреть сообщение
IGPIGP, Пожалуйста, прекратите вводить ТС в заблуждение.
Я не делал ничего такого злонамеренно. Поэтому, данную фразу отнесу к неряшливому применению русского языка.
Я и правда не нашёл ничего кроме 2-х методов показанных здесь:
Конструирование string из stringstream
При этом, я действительно не понимаю, как метод возвращающий строку по значению, может запустить её конструктор. Я бы предположил наличие третьей перегрузки метода, принимающей пару итераторов, но найти её не смог. Объектные файлы не просматривал.
Может объясните, как оно вообще компилируется? Почему, вернее. Остальное из сказанного в наших ответах достаточно похоже. igorrr37, первым сказал, что оба варианта не работают. Вы пришли третьим.
0
87 / 87 / 18
Регистрация: 11.06.2018
Сообщений: 302
11.06.2018, 14:51
Лучший ответ Сообщение было отмечено egorrich как решение

Решение

Цитата Сообщение от IGPIGP Посмотреть сообщение
При этом, я действительно не понимаю, как метод возвращающий строку по значению, может запустить её конструктор.
При возврате объекта по значению при отсутствии оптимизаций запускается конструктор копирования, или конструктор перемещения. Только при чем тут это?

Давайте я еще раз объясню в чем проблемы этого кода.

str() возвращает копию буфера в виде std::string (в вашей цитате - __string_type - это и есть std::string, вернее std::basic_string<T>, где T - это char в случае std::stringstream). У std::string есть методы, которые возвращают итераторы - begin() и end(). В выражении std::string::iterator s1 = ss.str().begin(); функция str() возвращает std::string - временный объект. Этот временный объект используется для получения итератора на начало строки. Затем этот итератор копируется в s1. В конце выражения временный объект уничтожается, а итератор остается "висячим". Это первая ошибка.
Во второй строке вызывается str() снова, где весь сценарий повторяется. Опять создается временный объект (не тот же самый, что в первый раз, но технически могущий использовать ту же самую память, по принципу, который я показал в предыдущем ответе - отсюда видимая работоспособность кода в некоторых случаях), мы получаем у него итератор на элемент, следующий за последним, итератор копируется в s2, временный объект умирает. Но итератор s2, который был получен, не имеет никакого отношения к последовательности, на которую указывает s1. Тем не менее, они оба используются для конструирования новой строки str1 посредством конструктора от двух итераторов (http://ru.cppreference.com/w/c... sic_string , #6 по ссылке). Это - вторая ошибка.

Надеюсь, теперь понятно, что сам по себе str() не играет никакой роли в проявлении этих ошибок.

Вот, для примера, корректный вариант написания оригинального кода ТС:
C++
1
2
3
4
5
6
7
8
9
10
   std::stringstream ss;
   ss.str ("Example string");
   
   std::string ssstr = ss.str();
 
   std::string::iterator s1 = ssstr.begin();
   std::string::iterator s2 = ssstr.end();
 
   std::string str1(s1, s2);
   std::cout << str1 << "\n";
И второй вариант:
C++
1
2
3
4
5
6
7
   std::stringstream ss;
   ss.str ("Example string");
 
   std::string ssstr = ss.str();
 
   std::string str1(ssstr.begin(), ssstr.end());
   std::cout << str1 << "\n";
Вариант с продлением жизни временного объекта посредством ссылки на константу:
C++
1
2
3
4
5
6
7
8
9
10
   std::stringstream ss;
   ss.str ("Example string");
   
   const std::string & ssstr = ss.str();
 
   std::string::iterator s1 = ssstr.begin();
   std::string::iterator s2 = ssstr.end();
 
   std::string str1(s1, s2);
   std::cout << str1 << "\n";
Цитата Сообщение от IGPIGP Посмотреть сообщение
Может объясните, как оно вообще компилируется? Почему, вернее.
А что, собственно, вас смущает? То, что у временных объектов можно вызвать функции, способные вернуть ссылку на внутреннее состояние, которое станет невалидным после завершения времени жизни объекта? Диагностировать такие вещи в общем случае обязанность программиста. Это фундаментальная особенность дизайна языков С и С++. Они не контролируют слишком много для того, чтобы не заставлять программиста переплачивать за проверки в тех случаях, когда он знает, что делает. А такие вот случаи, когда программист не знает, что он делает, и пишет подобный код с UB, легко искореняются практикой.

Пример на тему:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class String
{
public:
     char * begin() { return buf_; }
     char * end() { return buf_ + 10; }
 
private:
     char buf_[10];
};
 
String str()
{
     return {};
}
 
int main()
{
     char * s1 = str().begin(); // у String вызывается ~String() в конце этого выражения
                                // и buf_ перестает быть валидной памятью. Указатель s1 - "висячий"
     char * s2 = str().end(); // тоже самое
}
Он иллюстрирует абсолютно те же ошибки, что и в стартовом посте, но без лишних сущностей, чтобы не было соблазна свалить всю вину на них.

Цитата Сообщение от IGPIGP Посмотреть сообщение
igorrr37, первым сказал, что оба варианта не работают. Вы пришли третьим.
Ответ igorrr37 был точным и верным. Одного его в теории достаточно, чтобы разобраться в ситуации. Но далее по дискуссии стало понятно, что ни вы, ни ТС не поняли содержания этого ответа, поэтому я решил написать свой, развернутый. Второй плюс к ответу igorrr37 поставил я.

Не по теме:

Цитата Сообщение от IGPIGP Посмотреть сообщение
Я не делал ничего такого злонамеренно. Поэтому, данную фразу отнесу к неряшливому применению русского языка.
Прошу меня простить за возможную обиду, которую могут вызвать мои последующие слова, но вы начали делать выводы и пытаться дать ответ ТС заранее зная, что неуверенно разбираетесь в теме. И ваши последующие вопросы только это подтверждают. Поэтому злонамеренно или нет, но в заблуждение вы автора вводили прекрасно отдавая себе отчет в том, что вы делаете. Он видит ваш рейтинг, и слепо доверяет вашим словам. Вы, например, заявили выше, что это баг. А также почему-то стали ссылаться на перегруженные функции, когда они совершенно не имеют отношения к вопросу. А еще я видел ваши промежуточные варианты ответов, которые вы уже переменили, где вы по ходу дела разбирались в исходниках, которые сами цитировали. Это можно простить зеленым новичкам, которые стремятся показать свои знания побыстрее, но в вашем исполнении я этого не понимаю.



Я не знаю, что еще тут можно добавить. Если что-то до сих пор не понятно, просто перечитайте все мои посты еще раз. Или прочитайте какие-нибудь книги, где описана эта ошибка.
1
1130 / 789 / 232
Регистрация: 12.04.2010
Сообщений: 2,012
11.06.2018, 18:46
Цитата Сообщение от ablex Посмотреть сообщение
str() возвращает временный объект. Временный. Объект. Что это означает для вашего
кода? Первое - это то, что строка, помещенная в этот временный
ablex, ss.str ("Example string"); sets the contents of the stream, discarding any previous contents. std::stringstream::str

Цитата Сообщение от egorrich Посмотреть сообщение
вопросом конструирования строки из stringstream
egorrich, значение ss.str() это string. Как из одной строки s1 построить другую s2. Ответ. s2 = s1.
C++
1
2
3
4
5
6
7
8
    std::stringstream ss;
   ss.str ("Example string");  // текст помещается в  stringstream
 
   ss << " abc ";  // еще есть оператор  <<
 
   std::string str1 = ss.str();  // вызывается конструктор объекта  str1
 
   std::cout << str1 << "\n";  // теперь мы можем использовать   string str1;
1
11.06.2018, 20:45

Не по теме:

Цитата Сообщение от ablex Посмотреть сообщение
Вы, например, заявили выше, что это баг. А также почему-то стали ссылаться на перегруженные функции,
Да. Поздновато было и показалось, что была попытка сделать вызов ss.str(it1,it2) То есть попытка инициализировать поток итераторами строки через вызов str(). Спать нужно было идти.
Цитата Сообщение от ablex Посмотреть сообщение
Прошу меня простить за возможную обиду
Принято.

0
0 / 0 / 0
Регистрация: 14.02.2016
Сообщений: 8
11.06.2018, 22:04  [ТС]
Всем большое спасибо за ответы. Ответ igorrr37 мне был понятен, только хотелось более развернутого и подробного пояснения. ablex еще раз большое спасибо за очень подробное описание проблемы. Alex5, я знаю, как получить нормальную строку из stringstream, в этой теме меня интересовал иной вопрос. Всем большое спасибо за ответы.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
11.06.2018, 22:04
Помогаю со студенческими работами здесь

Stringstream и оператор >>
Доброго времени. Код: #include &lt;iostream&gt; #include &lt;sstream&gt; using namespace std; int main()

Использование stringstream
Добрый день!

С# stringstream
Доброго времени суток! Не могу разобраться с отличием синтаксиса с С++ и С#. Подскажите, пожалуйста, как в С# правильно написать вот такой...

Создайте класс Animal. Добавьте поля string Name, string Kind, string Areal, int Population
Здравствуйте! По C# есть задачка, с которой я так и не разобрался :( Задача: Создайте класс Animal. Добавьте поля string...

Создайте класс Animal. Добавьте поля string Name, string Kind, string Areal, int Population
Создайте класс Animal. Добавьте поля string Name, string Kind, string Areal, int Population. Создание объектов может происходить как без...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Первый деплой
lagorue 16.01.2026
Не спеша развернул своё 1ое приложение в kubernetes. А дальше мне интересно создать 1фронтэнд приложения и 2 бэкэнд приложения развернуть 2 деплоя в кубере получится 2 сервиса и что-бы они. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит: токи, напряжения и их 1 и 2 производные при t = 0;. . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru