Search code examples
c++c++11templatestype-deduction

How do to have a template deduce its parameters


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.


Solution

  • 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.