请考虑以下代码:
#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
ing两个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输出其Hex表示,这导致关于原始文件内容的以下顺序:
30 20 30 20 30 20 30 0D 0A 30 20 30 20 30 20 30
正如你所看到的,除了0
s,space和carriage-return + line-feed
之外什么都没有。
知道为什么会发生这种情况吗?有关完整信息,我使用的是MinGW的GCC 8.2.0。
这是一个本地的:
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';
}
复制省略保证额外的对象将被完全删除或移动构造到位。