Reputation: 23
I want to make an int32_le_read(x)
manipulator that reads binary from a file and writes to x
. I implemented the int32_bin_manip
class
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);
};
for which operator>>
and operator<<
exist. How do I make a manipulator for istream
of it now? For the ostream
manipulator, I just created a function that returns an instance of this class and everything worked fine.
int32_bin_manip write_le_int32(int& x)
{
return int32_bin_manip(x);
}
But I can't do that with the istream
manipulator, because its argument is int32_bin_manip& obj
, unlike const int32_bin_manip& obj
, which means I can't create a function that returns an instance of int32_bin_manip
and use it like istream >> int32_le_read(x)
, because it will bind the temporary instance to a non-const reference which is forbidden. So how do I need to implement such a manipulator?
Upvotes: 2
Views: 75
Reputation: 82531
You could simply add another overload taking an rvalue reference to int32_bin_manip
; rvalue references work as lvalue references to non-const inside the function body of a function with the rvalue reference as argument.
Note: write
functionality removed for brevity in the following example, since there's no issue with using const
in this case.
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';
}
Depending on your design you may want to introduce a function with 2 seperate overloads for read
function calls on lvalues and rvalue references, if this suits your design better:
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;
}
};
Upvotes: 0
Reputation: 118097
You can't bind a non-const
lvalue reference to a temporary, but you don't have to since you a not modifying the object itself but instead another object that it's pointing at, so make the operator>>
take it by const&
too:
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
};
Upvotes: 3