不明白如何制作istream机械手

Can't understand how to make istream manipulator

提问人:Max Nov 提问时间:8/13/2023 最后编辑:paleonixMax Nov 更新时间:8/14/2023 访问量:59

问:

我想制作一个从文件中读取二进制文件并写入 .我实现了这个类int32_le_read(x)xint32_bin_manip

class bin_manip
{
    public:
     bin_manip();
     ~bin_manip();
     friend std::istream& operator>>(std::istream& is,bin_manip& obj);
     friend std::ostream& operator<<(std::ostream& os, const bin_manip& obj);
     virtual std::ostream& write(std::ostream& os) const = 0;
     virtual std::istream& read(std::istream& is) = 0;
};

class int32_bin_manip : public bin_manip
{
    public:
        int32_t* x;
        int32_bin_manip(int& x);
        std::ostream& write(std::ostream& os) const;
        std::istream& read(std::istream& is);
};

对于它和存在。我现在如何为它制作一个操纵器?对于操纵器,我刚刚创建了一个函数,该函数返回此类的实例,一切正常。operator>>operator<<istreamostream

int32_bin_manip write_le_int32(int& x)
{
    return int32_bin_manip(x);
}

但是我不能用操纵器这样做,因为它的参数是 ,不像 ,这意味着我不能创建一个返回 的实例的函数并像 一样使用它,因为它会将临时实例绑定到一个被禁止的非常量引用。那么我需要如何实现这样的操纵器呢?istreamint32_bin_manip& objconst int32_bin_manip& objint32_bin_manipistream >> int32_le_read(x)

C++ IO IOMarip 操纵器

评论

0赞 john 8/13/2023
您正在编写的这些类可能有用,但它们不是操纵器。操纵器不读取或写入数据,它们修改正在读取或写入的流。
0赞 john 8/13/2023
我看不出您有任何理由不能更改为 .您的“操纵器”是即使正在读取的数据不是 .friend std::istream& operator>>(std::istream& is,bin_manip& obj);friend std::istream& operator>>(std::istream& is,const bin_manip& obj);constconst
0赞 Max Nov 8/13/2023
@john 谢谢!这适用于 int32,但我有另一个操作器,它适用于 C 样式的字符串 (char*),在这种情况下,我需要修改指向字符串的指针(删除并为其分配新空间)放置在 manipulator 的类中。
0赞 john 8/13/2023
好吧,我想你可以拉同样的技巧,存储指向指针的指针。在最坏的情况下,您可以使用(甚至)。这些选项是可以的,只要您不使用它们来修改实际上 .这将是未定义的行为。const_castmutableconst

答:

3赞 Ted Lyngmo 8/13/2023 #1

你不能将非左值引用绑定到临时引用,但你不必这样做,因为你不是修改对象本身,而是修改它所指向的另一个对象,所以也要接受它:constoperator>>const&

class bin_manip {
public:
    virtual ~bin_manip() = default; // <- added
    friend std::istream& operator>>(std::istream& is, const bin_manip& obj);
    //                                                ^^^^^ added
    friend std::ostream& operator<<(std::ostream& os, const bin_manip& obj);

    virtual std::ostream& write(std::ostream& os) const = 0;
    virtual std::istream& read(std::istream& is) const = 0;
    //                                           ^^^^^ added
};

演示

评论

0赞 Max Nov 8/13/2023
谢谢!我理解这一点,但我认为如果我对 c 样式字符串做同样的事情,这个解决方案将不起作用,因为在这种情况下,我需要为指针重新分配空间。对不起,我当然应该在问题中写下这一点。
0赞 Ted Lyngmo 8/13/2023
@MaxNov嗯,我真的看不出那会是什么样子。也许您也可以将其添加到问题中?
0赞 Max Nov 8/13/2023
我找到了解决这个问题的方法,我认为现在不值得将其添加到原始问题中,但我会解释问题是什么。我想制作类似的操纵器,它必须像这样工作:istream >> read_c_str,其中 s 是 (char*)。在这种情况下,我必须将指向 s 的指针保存在我的操纵器类中,当调用>>运算符时,指针必须擦除,然后以新的长度再次创建(删除 s; s = new char[size_of_string]),因此我无法将读取函数标记为常量。
0赞 Max Nov 8/13/2023
但是我找到了另一种方法,我只是使用 s 作为缓冲区并在 s 的声明中为它分配足够的空间,然后我只是用 istream 中的新字符填充这个字符串,而无需重新分配这个指针@Ted Lyngmo
0赞 Ted Lyngmo 8/13/2023
在这种情况下,@MaxNov应该如此。但是,我不会假设它指向任何东西。deletedelete[]char*
0赞 fabian 8/13/2023 #2

您可以简单地添加另一个重载,将右值引用为 ;右值引用在函数的函数体中以右值引用为参数,作为对非常量的左值引用。int32_bin_manip

注意:为简洁起见,以下示例中删除了功能,因为在这种情况下使用没有问题。writeconst

class bin_manip
{
public:
    virtual std::istream& read(std::istream& is) = 0;

    friend std::istream& operator>>(std::istream& is, bin_manip& obj)
    {
        obj.read(is);
        return is;
    }
};

class int32_bin_manip : public bin_manip
{
    int32_t& m_x;
public:
    constexpr int32_bin_manip(int32_t& x)
        : m_x(x)
    {
    }

    std::istream& read(std::istream& is) override
    {
        is >> m_x;
        return is;
    }

    friend std::istream& operator>>(std::istream& is, int32_bin_manip&& obj)
    {
        obj.read(is);
        return is;
    }
};

int main() {
    int32_t i;
    std::cin >> int32_bin_manip(i);
    std::cout << i << '\n';
}

根据您的设计,您可能希望引入一个具有 2 个独立重载的函数,用于对左值和右值引用的函数调用,如果这更适合您的设计:read

class bin_manip
{
public:
    virtual std::istream& read(std::istream& is) & = 0; // this one works just for lvalues, not for rvalue references

    friend std::istream& operator>>(std::istream& is, bin_manip& obj)
    {
        obj.read(is);
        return is;
    }
};

class rvalue_enabled_bin_manip : public bin_manip
{
public:

    using bin_manip::read;

    virtual std::istream& read(std::istream& is)&& // overload for rvalues
    {
        // fall back to standard, non-rvalue
        return read(is);
    }

    friend std::istream& operator>>(std::istream& is, rvalue_enabled_bin_manip&& obj)
    {
        std::move(obj).read(is);
        return is;
    }
};

class int32_bin_manip : public rvalue_enabled_bin_manip
{
    int32_t& m_x;
public:
    constexpr int32_bin_manip(int32_t& x)
        : m_x(x)
    {
    }

    std::istream& read(std::istream& is) & override
    {
        is >> m_x;
        return is;
    }
};