Search code examples
c++templatesiteratorc++14default-constructor

How to default construct value_type of iterator as default argument in the function?


Here is the abomination:

template <typename BidirIt, typename OutputIt, typename T,
          typename BinaryDoOp, typename BinaryUndoOp>
void sliding_window(BidirIt first, BidirIt last, OutputIt d_first,
                    typename std::iterator_traits<BidirIt>::difference_type length,
                    T init = typename std::iterator_traits<BidirIt>::value_type(),
                    BinaryDoOp op = std::plus<>{},
                    BinaryUndoOp undo = std::minus<>{})

So I want T to be std::iterator_traits<BidirIt>::value_type by default and default construct an object of that type giving it a name init.

After solving a problem of fitting some of the variable types in one line, I found that compiler is not able to deduce T, here is what it exactly says:

error: no matching function for call to 'sliding_window' sliding_window(v.begin(), v.end(), output.begin(), window_length/, 0, std::plus<>(), std::minus<>()/);

note: candidate template ignored: couldn't infer template argument 'T' void sliding_window(BidirIt first, BidirIt last, OutputIt d_first,

My compiler is clang++-3.9.

Call site code:

std::vector<int> v(window_length + window_count - 1);
std::iota(v.begin(), v.end(), 0);

std::vector<int> output(window_count);
std::vector<int> correct_result{3, 6, 9, 12};

sliding_window(v.begin(), v.end(), output.begin(), window_length/*, 0, std::plus<>(), std::minus<>()*/);

When the commented part is uncommented code works correctly.

From what I know about templates, it should be able to deduce that type since it is practically a default constructor call there, which should yield std::iterator_traits<BidirIt>::value_type. Do I have any misunderstanding of how type of default arguments work when function is templated on a type?

Question: how to fix it? It would be great to add some explanations to it too.


Solution

  • Template types cannot be deduced from defaulted templated arguments: C++14 lambda's default argument type deduction depending on preceding arguments. As far as how to fix it, you would need to default the types:

    template <typename BidirIt, typename OutputIt,
        typename T = typename std::iterator_traits<BiDirIt>::value_type,
        typename BinaryDoOp = std::plus<>,
        typename BinaryUndoOp = std::minus<>>
    void sliding_window(BidirIt first, BidirIt last, OutputIt d_first,
                    typename std::iterator_traits<BidirIt>::difference_type length,
                    T init = T{},
                    BinaryDoOp op = BinaryDoOp{},
                    BinaryUndoOp undo = BinaryUndoOp{})
    

    Another approach would also be to have overloaded functions, where the ones with fewer template parameters/arguments call the ones with more and handle the defaulting at the call site. This is the approach used by std::accumulate: http://en.cppreference.com/w/cpp/algorithm/accumulate. Then you'd have multiple functions so there's some repetition there, but each one is quite a bit more readable.