Как инициализировать std:: stringstream?

Мне нужно объединить строку с целыми числами. Для этого я использую stringstream следующим образом:

int numPeople = 10;
stringstream ss;
ss << "Number of people is " << numPeople;

И это сработало. Но я пытался сделать это следующим образом:

int numPeople = 10;
stringstream ss << "Number of people is " << numPeople;

И я получал следующую ошибку: " ожидаемый инициализатор до < < < Маркер"

Почему я получил эту ошибку? Почему я не могу присвоить значение stringstream в то же время, я объявляю его?

Ответы

Ответ 1

stringstream ss << "Number of people is " << numPeople;

Почему я не могу присвоить значение stringstream в то же время, я объявляю его?

Это похоже на надежду, что это сработает:

int x + 3 + 9;

Проблема

Только С++ позволяет предоставить значение конструктору при создании объекта, а конструктор принимает список выражений, разделенных запятыми. Для удобства нотация Type variable = value; позволяет вызвать Type variable(value);, но работает только для одного значения.

Для int вы можете легко исправить код:

int x = 3 + 9;

... и он работает, потому что сначала можно оценить значение "3 + 9", чтобы получить нормальное значение для хранения в x. Поведение компилятора для оператора + на int делает то, что мы хотим. Производит int результат. Но если вы попробуете это для stringstream...

stringstream ss = "Number of people is " << numPeople;  // BROKEN

... это не сработает, потому что "Number of people is " << numPeople является незаконным - вы получите сообщение об ошибке "error C2296: '<<' : illegal, left operand has type 'const char [20]'" - это не даст полезного значения для конструктора stringstream. Проблема заключается в том, что компилятор все еще пытается применить оператор поразрядного сдвига, что имеет смысл только для чисел, так как перегрузки для <<, которые мы хотели бы применить, требуют левого аргумента типа ostream&. С++ требует, чтобы значение, оцениваемое справа от =, оценивалось независимо от назначения, которое в конечном итоге было выполнено с полученным значением.

Решение

Это немного проблема с курицей и яйцом, потому что вам нужно объединить нужные значения в stringstream, чтобы вызывать конструктор stringstream, но для этого вам нужно.. a stringstream. Фактически вы можете снять это с помощью временного stringstream:

static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople)

К сожалению, приведение роли происходит потому, что перегрузка operator<< обрабатывает stringstream через ссылки на их базовый класс ostream, возвращая ostream&, поэтому вам нужно вручную вернуться к типу stringstream, так что вы может затем вызвать конструктор перемещения std::stringstream...

Полная однострочная конструкция тогда...

std::stringstream ss(static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople));

... но это слишком ужасно, чтобы созерцать.

Создание решения (возможно) менее отвратительного

В зависимости от вашей чувствительности вы можете почувствовать, что макрос помогает или хуже...

#define OSS(VALUES) \
    static_cast<std::ostringstream&&>(std::ostringstream() << VALUES)

std::stringstream ss(OSS("Number of people is " << numPeople));

FWIW, вы также можете использовать макрос для создания строк...

std::string s(OSS("Number of people is " << numPeople).str()); 

Лучшая практика (возможно)

Просто создайте stringstream - опционально предоставляя одиночный string конструктору, а затем используйте operator<< во втором выражении:

std::stringstream ss;
ss << "Number of people is " << numPeople;

Это гораздо легче читать. С построением перемещения, после оптимизации, вероятно, нет причин для оптимизации двух операторов.

Альтернативный

С++ 11 внесли to_string() перегрузки, которые удобны, если у вас есть целое значение или два, чтобы совместить с или в string:

std::string s = "Number of people is " + std::to_string(numPeople);

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

Это было хуже в С++ 03

С++ 03 не хватало конструкторов перемещения, поэтому необходимо было использовать функцию std::ostringstream::str() на временном, чтобы получить std::string, с помощью которого можно создать названный stringsteam...

stringstream ss(static_cast<std::ostringstream&>(std::ostringstream() << "Number of people is " << numPeople).str());

С помощью этого кода С++ 03 существует вероятность дублирования распределений динамической памяти и копии содержимого, поэтому лучше всего было бы выполнить построение с последующим потоком.

Ответ 2

Строковый бит должен быть инициализирован до того, как к нему могут быть добавлены значения. stringstream - это объект.

Выполняя:

std::stringstream ss;

Фактически вы позволяете приложению распределять пространство для обработки добавленных значений.