I am trying to write a sink for a back_inserter
to reduce the amount of std::copy()
commands that proliferate the code.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
template <typename OutputIterator>
class sink
{
public:
sink(OutputIterator out) : _out(out) { }
OutputIterator _out;
};
template <typename OI, typename C>
sink<OI>& operator<<(sink<OI>& s, const C& c)
{
std::copy(c.begin(), c.end(), s._out);
return s;
}
int main(int, const char*[])
{
std::vector<uint8_t> c;
// auto s = sink<std::back_insert_iterator<std::vector<uint8_t>>>(std::back_inserter(c));
auto s = sink(std::back_inserter(c));
s << std::vector<uint8_t>{'F','e','e','d','i','n','g',' ','f','r','o','g','g','i','e','s'};
s << std::string("Hungry hippos");
std::copy(c.begin(), c.end(), std::ostream_iterator<int>(std::cout, ":"));
}
However this gives the error:
main.cpp: In function 'int main(int, const char**)':
main.cpp:27:18: error: missing template arguments before '(' token
auto s = sink(std::back_inserter(c));
^
The code below works but is less than desirable as it looks worse than having lots of std::copy
function calls everywhere.
auto s = sink<std::back_insert_iterator<std::vector<uint8_t>>>(std::back_inserter(c));
How do I give the hint to the compiler to deduce the type automatically?
Eventually I would like to evolve the system so I can simply write
sink(out) << reply::stock_reply(reply::bad_request);
where out
is a back_insert_iterator
and stock_reply
gives a canned response.
C++17 added this exact feature, along with surrounding help in cases where disambiguation is necessary. It's called class template argument deduction.
If that is not an option, you can resort to the workarounds such as "make" helper functions. This is what this would look like in your case (the forward
is not strictly necessary for you here):
template<typename OutputIterator>
sink<std::decay_t<OutputIterator>> make_sink(OutputIterator&& output_iterator)
{
return sink<std::decay_t<OutputIterator>>(std::forward<OutputIterator>(output_iterator));
}
Live demo here. The std::decay_t
is so that the function does the right thing when passed an lvalue.