考虑以下代码:
#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
,空格和carriage-return + line-feed
。
知道为什么会这样吗?有关完整信息,我正在使用MinGW的GCC 8.2.0。
答案 0 :(得分:4)
这是本地的:
std::stringstream training{};
这将返回对表示为本地的引用,并包装在元组中:
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';
}
复制省略确保多余的对象将被完全删除或移动到位。