Связанный со структурой std :: tuple breaks std :: stringstream - PullRequest
2 голосов
/ 17 марта 2019

Рассмотрим следующий код:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <tuple>

auto read_data(std::ifstream& training_file) {
    if (!training_file) {
        throw std::runtime_error{"Error: could not open one or more files"};
    }

    std::stringstream training{};
    training << training_file.rdbuf();

    std::cout << training.str() << '\n';

    return std::tie(training);
}

int main() {
    std::ifstream input{"input.txt"};

    auto [train] = read_data(input);

    std::cout << train.str() << '\n';
    std::cout << "x" << '\n';
}

И не обращайте внимания на тот факт, что я возвращаю один элемент с std::tie (изначально я tie использую два std::stringstream объекта, но это не нужнодля MCVE).

Файл input.txt выглядит следующим образом:

0,0,0,0,
0,0,0,0,

Примечание - после второй строки нет разрыва строки.

Неожиданный вывод этой программы:

0,0,0,0,
0,0,0,0,
É$~      Ź     ,
x

Очевидно, что часть É$~ Ź , не должна была быть там.

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

Становится еще более странным, когда играешь с кодом.Если я закомментирую std::cout << train.str() << '\n'; в main() и продублирую строку std::cout << training.str() << '\n'; в функции read_data(), результат будет таким, как ожидалось:

0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
x

Так что это не вызываетсяпозвонив std::stringstream::str дважды.Это должно быть вызвано использованием возвращаемого значения.

Что еще?Выполнение строки std::cout << train.str() << '\n'; из main() дважды приводит к завершению программы с code 3.

GDB сообщает:

gdb: unknown target exception 0x80000001 at 0x7ff909e845c0
Thread 1 received signal ?, Unknown signal.
0x00007ff909e845c0 in ?? ()

Но это все жене заканчивается здесь .Если я изменю содержимое файла так, чтобы оно содержало:

0 0

или

0 0 0 0
0 0 0 0

, вывод снова будет таким, как ожидалось (с использованием исходного кода - один cout в read_data() иодин в main().

Чтобы убедиться, что меня не обманул какой-то непечатаемый символ, проникший в мой файл, я использовал PowerShell для вывода его шестнадцатеричного представления, что привело к следующей последовательности относительноИсходное содержимое файла:

30 20 30 20 30 20 30 0D 0A 30 20 30 20 30 20 30

Как видите, в моем файле нет ничего, кроме 0 s, пробелов и carriage-return + line-feed.

Есть идеи, почему это может происходить?Для полной информации я использую GCC 8.2.0 от MinGW.

1 Ответ

4 голосов
/ 17 марта 2019

Это местный:

std::stringstream training{};

Возвращает ссылку на указанный local, завернутый в кортеж:

return std::tie(training);

Таким образом, auto [train] = ...; инициализирует имя train как свисающую ссылку. Поведение программы не определено.


Если вам нужно вернуть два потока (или больше), просто предварительно объявите их в выбранном вами агрегате кортеж / массив / пользовательский набор:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <tuple>
#include <array>

auto read_data() {
    std::array<std::stringstream,2> trainings{
        std::stringstream{},
        std::stringstream{}
    };

    return trainings;
}

int main() {

    auto train = read_data();

    std::cout << train[1].str() << '\n';
    std::cout << "x" << '\n';
}

Живой код

Copy elision гарантирует, что дополнительный объект будет либо полностью удален, либо перемещен на свое место.

...