I thought the call of the operator<< would generate a two-parameter function call. So, why does this not compile, then?
#include <iostream> // ostream
#include <iomanip> // setw, setfill
using std::ostream; using std::setw; using std::setfill;
struct Clock {
int h_, m_, s_;
Clock(int hours, int minutes, int seconds)
: h_{hours}, m_{minutes}, s_{seconds} {}
void setClock(int hours, int minutes, int seconds) {
h_ = hours; m_ = minutes; s_ = seconds;
}
friend ostream& operator<<(ostream&os, const Clock& c) {
auto w2 = [](ostream&os, int f) -> ostream& {
return os << setw(2) << setfill( '0' ) << f; };
return os << w2(c.h_) <<':'<<w2(c.m_)<<':'<<w2(c.s_); // ERROR
}
};
The error is (gcc-6)
$ g++-6 -std=gnu++1y ...
file.cpp: In function ‘std::ostream& operator<<(std::ostream&, const Clock&)’:
file.cpp:745:33: error: no match for call to ‘(operator<<(std::ostream&, const Clock&)::<lambda(std::ostream&, int)>) (const int&)’
return os << w2(c.h_) <<':'<<w2(c.m_)<<':'<<w2(c.s_);
^
I also tried the call os << w2(os,c.h_) but gcc and I agreed that was nonsense. Also I tried the lambda as auto as possible:
auto w2 = [](auto&os, auto f) {
return os << setw(2) << setfill( '0' ) << f; };
also no luck.
Any hints?
I thought the call of the
operator<<would generate a two-parameter function call.
No, invoking an overloaded operator<< is basically the same as invoking a binary function:
a << b;
// is equivalent to
operator<<(a, b);
// or to
a.operator<<(b);
What you're trying to do is invoke operator<< using a lambda that returns an ostream& as the right-hand argument, but you're not passing the ostream& argument to the lambda itself.
os << w2(os,c.h_) is syntactically valid, but will not compile because there is no operator<<(ostream&, ostream&) definition.
What you can do is simply invoke the lambda without streaming it:
friend ostream& operator<<(ostream&os, const Clock& c) {
auto w2 = [](ostream&os, int f) -> ostream& {
return os << setw(2) << setfill( '0' ) << f; };
w2(os, c.h_);
os <<':';
w2(os, c.m_);
os << ':';
return w2(os, c.s_);
}
wandbox example
If you want to achieve your desired syntax, you will need a little more work. Here's a possible solution:
template <typename TF>
struct streamable : TF
{
streamable(TF&& f) : TF{std::move(f)} { }
};
template <typename TF>
auto& operator<<(ostream& os, const streamable<TF>& s)
{
s(os); return os;
}
template <typename TF>
auto make_streamable_impl(TF f)
{
return streamable<TF>(std::move(f));
}
template <typename TF>
auto make_streamable(TF f)
{
return [&](auto&& x) mutable
{
return make_streamable_impl([&](ostream& os) -> auto&
{
f(os, x);
return os;
});
};
}
Usage:
friend ostream& operator<<(ostream&os, const Clock& c) {
auto w2 = make_streamable([](ostream&os, int f) -> ostream& {
return os << setw(2) << setfill( '0' ) << f; });
return os << w2(c.h_) <<':'<<w2(c.m_)<<':'<<w2(c.s_);
}
wandbox example
Note that a real implementation should probably perfectly-capture the arguments into the lambdas.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With