1

I have a template function with two template parameters.

The first (T) is deduced based on the type of the first parameter past to the function. The second (ItrT) is deduced by use of std::type_traits and T.

When I use ItrT as the type for a parameter (see function bar) than all types are deduced implicitly, but when I use std::function<void(ItrT)> as the type of a parameter (see function foo) than the correct types can only be deduced when fully specifying all template parameter(Even if using the exact same code as in the function definition). One would imagine that using ItrT inside a template wouldn't change the compilers ability to deduce the template.

Why isn't this the case? And what do I need to do, so that all template parameter can be implicitly deduced?

I'm using C++17.

template <class T, class ItrT = typename std::iterator_traits<T>::value_type>
auto foo(T iterator, std::function<void(ItrT)> expr) -> void{
}

template <class T, class ItrT = typename std::iterator_traits<T>::value_type>
auto bar(T iterator, ItrT expr) -> void{
}

int main() {
    std::vector<int> vec = {1, 2, 3};

    bar(vec.begin(), 1); // Compiles!

    foo(vec.begin(), [](int) {}); // Failes!

    foo<decltype(vec.begin()),
        std::iterator_traits<decltype(vec.begin())>::value_type>
        (vec.begin(), [](int) {}); // Compiles!
}
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
batburger
  • 89
  • 6

1 Answers1

3

I'm guessing the confusion here is around the role of default template arguments.

The rule is not: try to deduce a parameter. If deduction fails, then use the default (if provided).

Rather, the rule is: if the parameter is in a deduced context, deduce it. If deduction fails, abort. If it's not in a deduced context, and it's not explicitly provided, use the default argument. In other words, the default argument is used only if the parameter is neither in a deduced context nor explicitly provided.

In both your examples, ItrT is in a deduced context, so the default template argument is not considered at all. The difference between the two is that you can deduce T from a lambda (you just match its type) but you cannot deduce function<void(T) from a lambda - a lambda can be converted to an appropriate function, but a lambda is not a function. Template deduction doesn't do conversions. Template deduction just matches patterns.

Barry
  • 286,269
  • 29
  • 621
  • 977