Home > Software engineering >  how does this template parameter deduction work?
how does this template parameter deduction work?

Time:11-29

How did compiler decide to call bar function without knowing the type of template parameter T of foo function? Usually when we call foo(2), T is deduced as int based on argument 2. Here T is deduced based on parameters of function bar to which foo is passed.

#include <iostream>

template<typename T>
void foo(const T& a_) {
    std::cout << a_<< std::endl;
}

void bar(void (*ptr) (const int&)) {
    std::cout << "bar called" << std::endl;
}

int main() {
    bar(foo);
}

compiler: gcc

CodePudding user response:

When you have an overload set like foo, meaning that the result of name lookup for foo results in (potentially multiple) non-template functions or function templates and the overload set has its address or a reference taken (here implicitly by passing it as a function argument), then the compiler will try to do overload resolution against the target type if there is any. The target type here is void(*)(const int&). The whole procedure is specified in [over.over], parts of which I explain below.

The way this works is that the compiler will look at each function and function template in the overload set, will look whether it can match its type against the target type and will then add those functions and function template specializations that match to a set of selected overloads. Afterwards a few more elimination steps are done to reduce the set of selected overloads (e.g. checking whether associated constraints are satisfied and preferring functions over function template specializations, see [over.over]/5 for all details) and hopefully at the end exactly one selected function or function template specialization remains, which will then be chosen as the result of overload resolution that the original name (foo) refers to and from which the address/reference is taken.

For a non-template function a match means that the function type of the target ( void(const int&)) needs to simply be identical to the function's type. But you don't have any non-template overload here.

For a function template the compiler will try to perform template argument deduction to select one specialization of the template that matches the type and can be added to the list of selected overloads. Specifically this works just the same as in template argument deduction of a function call where there is usually a list of function argument/parameter pairs to deduce template arguments from, but in this case (given that there is a target type) the deduction will consider only one argument/parameter pair with the argument being the target type (void(*)(const int&)) and the parameter being the function type of the template containing the template parameters, i.e. here void(const T&), adjusted to a pointer type void(*)(const T&) because the argument is a function pointer type.

So in the end the compiler will perform normal deduction of

void(*)(const int&)

against

void(*)(const T&)

to decide what T should be in the selected specialization. I guess it is pretty obvious that T should be int for the types to match. So the selected specialization will be the one with T = int which has a function type matching the target type and since it is the only selected overload and since it will not be eliminated in any of the further steps, it will be chosen as the result of overload resolution.

CodePudding user response:

It actually deduces the parameter of T from the type of function pointer, how this basically works.

  • At first compiles sees that you called bar, which accepts function pointer which returns void and takes one argument of const int& type.
  • Then it sees that passes parameter is function template. Then it starts to wonder if you can instantiate the function so that it's return type and parameter list is in correspondence with whatever the function pointer type is.
  • Voila. The compiler indeed can achieve this by removing cv qualifiers and references. The T appears to be int.
  • Related