kmiklas
kmiklas

Reputation: 13433

No matching function for call to find in templated linked list

Complier error "No matching function for call to" in my case a find function in a linked list.

Why this error?

Function

template<class T>
int find(std::shared_ptr<Node<T>> ptr, T val) {
    int pos(0);
    while (ptr) {
        if (ptr->val == val)
            return pos;
        else {
            ptr = ptr->next;
            pos++;
        }
    }
    return -1; // Not found
}

Invocation

std::cout << "Pos: " << find(myFloat, 9.83) << std::endl;

myFloat is a shared_ptr, and root node for a list of floats, successfully populated as follows:

Linked List of Floats

2.5 ⟹ 3.7 ⟹ 4.8 ⟹ 7.93 ⟹ 0.96 ⟹ 9.83 ⟹ 7.45

Struct

template<class T>
struct Node {
    Node(T k):val(k){}
    T val;
    std::shared_ptr<Node<T>> next = nullptr;
};

Error

No matching function for call to 'find'

myFloat Definition

std::shared_ptr<Node<float>> myFloat = { std::make_shared<Node<float>>(0.) };

Upvotes: 1

Views: 372

Answers (1)

aschepler
aschepler

Reputation: 72356

Since you didn't provide any explicit template arguments to find (as in find<float>(myFloat, 9.83)), the compiler must deduce the template argument T from the function arguments.

During template argument deduction, every appearance of a template parameter in the function parameter types is either in a deduced context or non-deduced context. In your case, both are considered deduced context. A type or value is determined for each appearance of a template parameter in a deduced context.

myFloat has type std::shared_ptr<Node<float>> and the first function parameter has type std::shared_ptr<Node<T>>, so T is deduced as float.

9.83 has type double, and the second function parameter has type T, so T is deduced as double.

Since template parameter T was deduced to be two different types, the overall deduction fails! When the same template parameter is deduced more than once, all the results must be identical.

So what can you do to make this just work?

An unpleasant solution would be to leave the template alone and require callers to use it carefully. find<float>(myFloat, 9.83) would work by explicitly providing the template argument. find(myFloat, 9.83f) would work by changing the type of the second function argument. But it's better to make the template more flexible and friendlier.

One simple fix is to just use two different template parameters.

template<class T, class U>
int find(std::shared_ptr<Node<T>> ptr, U val);

Then the function simply requires that the == operator works for the types T and U.

Or if you want the second function argument to be implicitly converted to the type used in the Node, you could intentionally turn the second use of T into a non-deduced context:

template <typename T>
struct identity {
    using type = T;
};

template<class T>
int find(std::shared_ptr<Node<T>> ptr,
         typename identity<T>::type val);

By appearing left of ::, the second T is now a non-deduced context, so the expression find(myFloat, 9.83) will deduce T from the first argument only, resulting in T=float, then substitute into the template function declaration. typename identity<float>::type works out to float, so there will be an implicit conversion from the double literal 9.83 to float to call the function.

Upvotes: 3

Related Questions