Why is this template inference failing

268 views Asked by At

This code won't compile with clang++ 6.0 or g++4.9.1 (Code has no meaning but this is the minimum example that makes it happen):

#include <forward_list>

template<typename T>
T getItem(typename std::forward_list<T>::const_iterator it) {
    return *it;
}

template<typename T>
void foo() {
    std::forward_list<T> list;
    auto item = getItem(list.cbegin());
}

template<typename T>
void bar(const std::forward_list<T>& list) {
    auto item = getItem(list.cbegin());
}

int main() {
    std::forward_list<int> list;
    bar(list);
}

I get this error

t2.cpp:17:17: error: no matching function for call to 'getItem'
    auto item = getItem(list.cbegin());
                ^~~~~~~
t2.cpp:22:5: note: in instantiation of function template specialization 'bar<int>' requested here
    bar(list);
    ^
t2.cpp:4:3: note: candidate template ignored: couldn't infer template argument 'T'
T getItem(typename std::forward_list<T>::const_iterator it) {
  ^
1 error generated.

To fix it I need to change bar()'s call like this:

template<typename T>
void bar(const std::forward_list<T>& list) {
    auto item = getItem<T>(list.cbegin());
}

I don't understand why the compiler is not able to infer the template argument, and the strange thing is that the compiler is perfectly happy with foo().

1

There are 1 answers

0
Marco A. On BEST ANSWER

You're trying to deduce a template argument from a non-deduced context, § [temp.deduct.type]/5

The non-deduced contexts are:

— The nested-name-specifier of a type that was specified using a qualified-id.

i.e.

template<typename T>
T getItem(typename std::forward_list<T>::const_iterator it)
                   ^^^^^^^^^^^^^^^^^^^^

and § [temp.deduct.type]/4

In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

If you try to instantiate foo it will give the same error. You're not getting errors for foo with the code above since dependent names are only looked up at instantiation (this is commonly referred to as two-phase lookup). Cfr. Semantic correctness of non-instantiated C++ template functions