Consider the following code:
#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';
}
And ignore the fact that I am returning a single element with std::tie
(originally I am tie
ing two std::stringstream
objects, but that's not needed for the MCVE).
The input.txt
file looks like this:
0,0,0,0, 0,0,0,0,
Note - the is no line break after the second line.
The unexpected output of this program is:
0,0,0,0, 0,0,0,0, É$~ Ź , x
Obviously, the É$~ Ź ,
part should not have been there.
Notice that I am outputting the very same file contents. I am clueless where did the unexpected part come from.
It gets even stranger when playing with the code. If I comment out the std::cout << train.str() << '\n';
in main()
and duplicate the std::cout << training.str() << '\n';
line in read_data()
function, the output is as expected:
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, x
So it's not causaed by calling std::stringstream::str
twice. It must be caused by the usage of the returned value.
What's more? Executing the std::cout << train.str() << '\n';
line from main()
twice results in program termination with code 3
.
GDB reports:
gdb: unknown target exception 0x80000001 at 0x7ff909e845c0
Thread 1 received signal ?, Unknown signal.
0x00007ff909e845c0 in ?? ()
But it still does not end here. If I change the file content to contain either:
0 0
or
0 0 0 0
0 0 0 0
the output is again as expected (using the original code - one cout
in read_data()
and one in main()
.
To make sure I wasn't being tricked by some non-printable character that snuck into my file, I used PowerShell to output its Hex representation, which resulted in the following sequence regarding the original file content:
30 20 30 20 30 20 30 0D 0A 30 20 30 20 30 20 30
As you can see, nothing except for 0
s, spaces and carriage-return + line-feed
is in my file.
Any idea why this could be happening? For the full information, I am using GCC 8.2.0 from MinGW.
This is a local:
std::stringstream training{};
This returns a reference to said local, wrapped in a tuple:
return std::tie(training);
So auto [train] = ...;
initializes the name train
to be a dangling reference. The behavior of the program is undefined.
If you need to return two streams ( or more ) then just predaclare them in the tuple/array/custom aggregate of your choice:
#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 guarantees the extra object will either be removed altogether or move constructed into place.
User contributions licensed under CC BY-SA 3.0