首页 > 解决方案 > 结构绑定 std::tuple 中断 std::stringstream

问题描述

考虑以下代码:

#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';inmain()并复制std::cout << training.str() << '\n';line inread_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

输出再次符合预期(使用原始代码 - 一coutread_data()一进main().

为了确保我没有被一些潜入我的文件的不可打印字符所欺骗,我使用 PowerShell 输出其十六进制表示,这导致有关原始文件内容的以下序列:

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

如您所见,我的文件中除了0s、空格和之外什么都没有。carriage-return + line-feed

知道为什么会发生这种情况吗?有关完整信息,我使用的是 MinGW 的 GCC 8.2.0。

标签: c++stringstreamstdtuple

解决方案


这是本地的:

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';
}

实时代码

复制省略保证额外的对象要么被完全删除,要么被构建到位。


推荐阅读