【问题标题】:Structure-bound std::tuple breaks std::stringstream结构绑定 std::tuple 中断 std::stringstream
【发布时间】:2019-03-17 15:31:44
【问题描述】:

考虑以下代码:

#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 返回单个元素的事实(最初我是tieing 两个std::stringstream 对象,但MCVE 不需要)。

input.txt 文件如下所示:

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

注意 - 第二行之后没有换行符。

这个程序的意外输出是:

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

显然,É$~ Ź , 部分不应该存在。

请注意,我正在输出完全相同的文件内容。我不知道意外的部分是从哪里来的。

在玩代码时会变得更加陌生。如果我注释掉main() 中的std::cout &lt;&lt; train.str() &lt;&lt; '\n'; 并在read_data() 函数中复制std::cout &lt;&lt; training.str() &lt;&lt; '\n'; 行,则输出如预期:

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

所以这不是由两次调用std::stringstream::str 引起的。一定是使用了返回值造成的。

还有什么?从main() 执行std::cout &lt;&lt; train.str() &lt;&lt; '\n'; 行两次 会导致程序以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

输出再次符合预期(使用原始代码 - read_data() 中的一个 cout 和 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++ stringstream stdtuple


    【解决方案1】:

    这是本地人:

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

    Live Code

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

    【讨论】:

    • 好吧,这确实很愚蠢。从函数返回两个std::stringstreams 的优雅方式有什么想法吗? std::tuple 可以自己存储对象吗?我怀疑会有问题,因为std::stringstream 是不可复制的。我可以move 将其发送到包装器并返回吗?
    • @Fureeish - 按值返回。复制省略算法将完全摆脱额外的对象,或者将在目标处移动构造它。
    • 如果我只有 一个 std::stringstream 返回(如在原始问题中),那就太好了,但我注意到我最初使用 std::tie,因为我创建两个 流,我想返回它们并使用结构化绑定引用它们。编辑:当然我可以只调用read_data 两次并将结果存储在变量中,但我真的很想知道实现原始目标的方法,即使它毕竟不是首选做事方式
    • 谢谢!它非常有用,尤其是因为 可以 使用带有数组的结构化绑定(以及 std::array)。希望我能投票并接受两次!
    猜你喜欢
    • 1970-01-01
    • 2019-06-15
    • 2017-04-02
    • 2011-01-19
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多