how does this template parameter deduction work? - c++

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

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.

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

Meaning of "deduced A" in the context of type deduction from a call

If P is a class and P has the form simple-template-id, then the transformed A can be a derived class D of the deduced A.
from [temp.deduct.call]4.3
This sentence describes how a function template argument is still valid if it is derived from "deduced A", However, there is not solid definition for what "deduced A" actually is.
My theory is that deduced A is original P with template arguments from A substituted in, but this would break the rules of type deduction trying to find template arguments to make A and deduced A identical, as there would be cases with A being a non-reference and deduced A being a reference.
The goal of function template argument deduction is to figure out which particular specialization of a function template should be used in places where the template name is used like a function name. For example, given a function template
template <typename T>
void f(T* value) {}
when you then have a function call like
int* a = &x;
f(a);
the name f here is not actually the name of a function but the name of a function template. The compiler has to figure out which concrete specialization of the function template this call should actually be calling based on the types of the arguments given in the function call. In other words, it has to figure out which template argument X should be used for the template parameter T to get to an actual function f<X> that could be called here like that. This is a bit of an inverse problem compared to a normal function call. Rather than having to make a list of arguments fit a given signature (by applying conversions), we're now having to make a signature fit a given list of arguments. Another way of looking at it is as trying to deduce template arguments that will make the type of each function parameter match the type of each function call argument. This is what [temp.deduct.call]/4 is talking about here:
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A
Taking our example above, given some deduced template argument X, the deduced argument type is what we get by substituting our deduced X for T into our function parameter type T* (i.e., the type of argument this function parameter takes). If we deduce X to be int, substituting int for T into T* makes our deduced argument type come out to be int*. Since the deduced argument type int* is identical to the type of the actual argument, we've found that the function f<int> is what we were looking for.
To make all of this consistent with how normal function calls behave, there are a few corner cases to take care of. In particular with function call arguments of array and function types, where we normally have array-to-pointer and function-to-pointer decay, as well as top-level const. To deal with this, the standard specifies that the argument type A we're trying to match is not simply taken to be the type of the corresponding function call argument directly but is first transformed by applying the array-to-pointer, function-to-pointer, etc. conversions. This transformed A is the A we're actually trying to make our deduced argument type match. This is just to explain why the standard talks about a "transformed A" there. It's not really that important to the question at hand. The transformed A is just the function argument type we're actually trying to match.
Now, let's say we have some
template <typename T> class B {};
and some derived class
class D : public B<int> {};
When you then have a function template like
template <typename T>
void f(const B<T>*) {}
and a function call like this
D d;
f(&d);
there is no template argument X you could pick for T that would make the deduced argument type const B<X>* equal to D*. But since D is derived from B<int>, deducing the template argument to be int would nevertheless lead to a function specialization f<int> that could take the call. The whole paragraph [temp.deduct.call]/4.3 and especially the sentence from your question
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class D of the deduced A.
is there to allow exactly this to work…

Auto detection of template parameters without arguments

This is an offshoot of an answer to another SO post.
I have the following working code, with the expected output.
#include <iostream>
template <typename T>
T twice(T in)
{
return 2*in;
}
struct Foo
{
Foo operator+(int (*func)(int in)) const
{
Foo ret{data};
ret.data += func(ret.data);
return ret;
}
int data;
};
int main()
{
Foo f1{20};
Foo f2 = f1 + twice;
Foo f3 = f1 + twice<int>;
std::cout << f2.data << std::endl;
std::cout << f3.data << std::endl;
}
I did not know until yesterday that the compiler can deduce the type parameters of a function template even without an argument. In the code above, the expressions
f1 + twice
and
f1 + twice<int>
result in identical values.
My question is: Where in the C++03/C++11 standard can we find the necessary supporting documentation for the compiler's auto type detection logic?
C++11 14.8.2.2 Template arguments can be deduced from the type specified when taking the address of an overloaded function.
Here, the type specified by the parameter type of operator+ is int (*)(int), the overloaded function is twice, so int is deduced as the template argument to give a matching function type. See 14.8.2.5 if you need the gory details of that deduction.
This is closest to the actual c++11 standard but still openly available version of draft I've found: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf.
I believe the template parameter deduction is done when you write Foo f2 = f1 + twice; and pass twice as a function address. twice gets passed as an address of a function to the operator+. I believe the following mechanism kicks in:
14.8.2.2 Deducing template arguments taking the address of a function template
[temp.deduct.funcaddr]:
Template arguments can be deduced from the type specified when taking the address of an overloaded function (13.4). The function template’s function type and the specified type are used as the types of P and A, and the deduction is done as described in 14.8.2.5.
So the actual template deduction will be done basing on the type of the funct. I believe the relevant paragraphs from 14.8.2.5 are 1 and 2.
14.8.2.5 Deducing template arguments from a type [temp.deduct.type]`:
1 Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
2 In some cases, the deduction is done using a single set of types P and A, in other cases, there will be a set of corresponding types P and A. Type deduction is done independently for each P/A pair, and the deduced template argument values are then combined. If type deduction cannot be done for any P/A pair, or if for any pair the deduction leads to more than one possible set of deduced values, or if different pairs yield different deduced values, or if any template argument remains neither deduced nor explicitly specified, template
argument deduction fails.
Basically you pass twice as a pointer to the operator+, and then template arguments get deduced via type of the function as defined in operator+. So you have an actual type A that is int (*)(int in) and template types P of twice that gets matched against A and only int twice(int) fits.
I hope I got everything right.

C++ function type template parameter deduction rule

The following code when built with
clang -Wall main.cpp -o main.o
generates the following diagnostics (after the code):
template <typename F>
void fun(const F& f)
{
}
template <typename F>
void fun(F f)
{
}
double Test(double d) { return d; }
int main(int argc, const char * argv[])
{
fun(Test);
return 0;
}
diagnostics:
main.cpp:17:5: error: call to 'fun' is ambiguous
fun(Test);
^~~
main.cpp:2:6: note: candidate function [with F = double (double)]
void fun(const F& f)
^
main.cpp:8:6: note: candidate function [with F = double (*)(double)]
void fun(F f)
^
1 error generated.
The interesting part is not about the ambiguity error itself (that's not the major concern here). The interesting part is that the template parameter F of the first fun is resolved to be a pure function type of double (double), while the template parameter F of the second fun is resolved to be a more expected double (*)(double) function pointer type, when fun is invoked with a function name solely.
However, when we change the invocation of fun(Test) to be fun(&Test) to explicitly take the function's address (or explicit function pointer), then both fun resolve the template parameter F to be double (*)(double)!
This behavior seems to be a common one for all of Clang and GCC (and Visual Studio 2013).
The question is then: what's the function type template parameter deduction rule for template functions in the forms given in my example code?
PS: if we add another instance of fun to take F* f, then it seems the overloading rule just decides to pick this version, and no ambiguity is reported at all (even though, as I have already stated, the ambiguity is not the biggest concern earlier, but in this last case, I do wonder why the third version is the best match here?)
template <typename F>
void fun(F* f)
{
}
Probably you have already figured it out, as it has been almost 3 years since you posted that question. But I will give my answer in case you haven't.
The interesting part is that the template parameter F of the first fun is resolved to be a pure function type of double (double), while the template parameter F of the second fun is resolved to be a more expected double (*)(double) function pointer type, when fun is invoked with a function name solely.
First of all, bear in mind that arrays and functions are strange in that an array can implicitly decay into a pointer to its first element and functions can implicitly decay into function pointers. And although syntactically valid, function parameters cannot actually be of array or function type but pointers, meaning function parameters can be written with the type of array or functions, but compilers regard such types as pointers. For example look at the code below:
int val [3]; //type of val is 'int [3]'
int * pval = val; //type of pval is 'int *'
//the assignment is correct since val can decay into 'int *'
double foo(double); //type of foo is 'double (double)'
double (*pfoo) (double); // type of pfoo is 'double (*)(double)'
pfoo = foo; //correct since functions can decay into function pointers.
void bar(int x []); // syntax is correct
// but compilers see the type of x as 'int *'
void bar(int x(int));// again syntax is correct
// but compilers see the type of x as 'int (*)(int)'
However, things get even stranger when a function parameter has a reference type. A function parameter who has a type of reference to an array/function is regarded to have a type of reference to an array/function, not a pointer type. For instance:
void bar(int (& x)[2]); //type of x is now 'int (&) [2]'
void bar(int (& x)(int)); //type of x is now 'int (&)(int)'
Regarding your first question, since the type of the parameter in the first function of yours (fun(const F& f)) includes a reference, the type of f will be deduced as a reference to a function, when a function is passed as an argument; to be more precise, the deduced type of f will be double (&) (double). On the other hand, as the second function's parameter type does not include a reference (fun(F f)), compilers implicitly deduce the type of f as a function pointer (the deduced type of f will be double (*)(double)), when a function is passed as an argument.
However, when we change the invocation of fun(Test) to be fun(&Test) to explicitly take the function's address (or explicit function pointer), then both fun resolve the template parameter F to be double (*)(double)!
Well, now since you are explicitly passing a function pointer type as an argument (by taking the address of Test), the deduced type of f must have a pointer. However, the reference and the constantness of the first function's parameter is not ignored. When fun(&Test) is run, the deduced type of f for the first function will be double (* const &) (double) and the deduced type of f for the second function will be double (*) (double).
PS: if we add another instance of fun to take F* f, then it seems the overloading rule just decides to pick this version, and no ambiguity is reported at all (even though, as I have already stated, the ambiguity is not the biggest concern earlier, but in this last case, I do wonder why the third version is the best match here?)
(I deleted my prior answer for that part, please refer to below)
EDIT:
I gave a very sloppy answer for the question of how come there is no ambiguity any more when a third function (fun(F * f)) is added. Below is a clear answer, I hope.
The rule for resolving which function to pick up in the case of function templates is first to figure out the set of template specializations for a given argument. The reason for this is to eliminate the function templates that result in substitution failure as a candidate. Then based on the conversions from the argument to the parameters, worse matches are eliminated from the candidate pool of non-template functions and the valid template specializations. If a non-template and a template functions are equally good match, the non-template is picked up. If more than one template function are equally good match, then partial ordering rules are employed to eliminate the less specialized function templates. If one shines as the most specialized function template, then it is resolved; on the other hand if neither is more specialized, then the compiler issues an ambiguity error. Needless to say, if there is no valid candidate found, error is issued again.
Now let's point out again the template specializations for the argument Test. As mentioned above, after template type deduction the template specialization of the first function template is void fun(double (&f) (double) ) and that of the second function template is void fun(double (*f) (double) ). Based on the conversions needed from the argument type double (double) to the candidate template functions' parameter types double (&) (double) and double (*) (double) respectively, they both are regarded exact match since only trivial conversions are needed. Therefore, partial ordering rules are employed to distinguish which one is more specialized. It turns out that neither is, so ambiguity error is issued.
When you added the third function template (void fun(F * f)), the template type deduction produces the template specialization as void fun(double (*f)(double). As same before, all of the three template functions are equally good match (in fact they are exact match). Because of that again, partial ordering rules are employed as a last resort and it turns out that the third function template is more specialized and thus it is picked up.
Note on trivial conversions: Although not complete, the following conversions from argument type to parameter type are considered trivial conversion (given a type T):
from T to const T
from T to T &
or from array or function types to their corresponding pointer types (decays that are mentioned at the beginning).
EDIT #2: It seems that I may not be using the correct wordings, so just to be clear what I mean by function template is a template that creates functions and by template function a function that is created by the template.
Probably others can explain this better than me, but this is how I understand it (no quotes from the Standard, sorry).
One cannot copy a variable of function type around, so in template <typename F> void fun(F f), the F cannot have function type.
However, a variable of function type can be converted to a pointer to the function type (this is called "decay", like the array-to-pointer conversion), so when matching a function type with template <typename F> void fun(F f), F must be a pointer to a function.
When dealing with a reference to a function type, the function-to-pointer decay cannot happen (i cannot find this in the Standard, but it should be described together with the reference-to-array rules), so when matching template <typename F> void fun(const F& f), F is a function type (and the parameter's type is reference-to-function).

can't pass function pointer to method in parent class through a variadic function--compiler bug?

Say you have two structures, Generic_A and Generic_B. Generic_B is derived from Generic_A. Why is it that when Generic_B tries to access a method in its parent, Generic_A, it generates the following error:
test2.cpp: In function 'int main()':
test2.cpp:26: error: no matching function for call to 'case1(void (Generic_A::*)()'
This code, compiled using gcc version 4.4.6, replicates the problem:
#include <stdio.h>
struct Generic_A
{
void p1() { printf("%s\n", __PRETTY_FUNCTION__); };
};
struct Generic_B : public Generic_A
{
void p2() { printf("%s\n", __PRETTY_FUNCTION__); };
};
template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) ) {
printf("%s\n", __PRETTY_FUNCTION__);
}
template <class T>
void case2( void (T::*p)() ) {
printf("%s\n", __PRETTY_FUNCTION__);
}
main()
{
//generates error
case1<Generic_B>(&Generic_B::p1);
//compiles fine
case2<Generic_B>(&Generic_B::p1);
}
The only apparent difference between the two function calls is that case1() has a template argument parameter, and case2() doesn't. Shouldn't they both allow you to pass a function pointer to a method in Generic_B's parent (ie &Generic_B::p1)?
Also, casting the function pointer in case1 seems to sometimes resolve the error:
case1<Generic_B>( (void (Generic_B::*)()) &Generic_B::p1);
What is going on?
This is tricky, but it turns out g++ is correct.
First, the type of expression &Generic_B::p1 is void (Generic_A::*)(). The compiler uses Generic_B:: to qualify its name lookup and finds the member of Generic_A. The expression type depends on the definition of the member found, not the type used within the qualified-id.
But it's also legal to have
void (Generic_B::*member)() = &Generic_B::p1;
since there is an implicit conversion from void (Generic_A::*)() to void (Generic_B::*)().
Whenever a function template is used as a function call, the compiler goes through three basic steps (or attempts to):
Substitute any explicit template arguments for the template parameters in the function declaration.
For each function parameter that still involves at least one template parameter, compare the corresponding function argument to that function parameter, to (possibly) deduce those template parameters.
Substitute the deduced template parameters into the function declaration.
In this case, we have the function template declaration
template <class T,class... ARGS>
void case1( void (T::*p)(ARGS...) );
where the template parameters are T and ARGS, and the function call expression
case1<Generic_B>(&Generic_B::p1)
where the explicit template argument is Generic_B and the function argument is &Generic_B::p1.
So step 1, substitute the explicit template argument:
void case1( void (Generic_B::*p)(ARGS...) );
Step 2, compare parameter types and argument types:
The parameter type (P in Standard section 14.8.2) is void (Generic_B::*)(ARGS...). The argument type (A) is void (Generic_A::*)().
C++ Standard (N3485) 14.8.2.1p4:
In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:
If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the transformed A.
The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification conversion (4.4).
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.
So type deduction allows for certain implicit conversions involving const / volatile and/or derived-to-base conversions, but implicit conversions of pointers to members are not considered.
In the case1 example, type deduction fails, and the function is not a match.
Unfortunately, there's no way to explicitly specify that your template parameter pack ARGS should be substituted with an empty list. As you already discovered, you can get this working by explicitly doing the necessary pointer to member function conversion yourself, even though it's otherwise valid as an implicit conversion.

Overload ambiguity when passing R-value to function that takes L-value

I have 2 overloaded functions - one takes an L-value, and the other takes an R-value. The purpose is so that the function can be called like:
Obj obj;
foo(obj);
OR:
foo(Obj());
So, I write 2 overloaded functions:
template <class T>
void foo(T& v)
{
/* ... function body code goes here ... */
}
template <class T>
void foo(T&& v)
{
foo(v);
}
int main()
{
foo(int(5));
}
The R-value overload merely needs to delegate to the L-value overload. The way I understand it, once I'm in the body of the function, any use of v gives me an L-value reference, unless I specifically use std::move or std::forward. So calling foo(v) within the R-value overload should automatically call the L-value version (rather than recursing.)
But, the compiler complains about ambiguity:
test.cpp: In function ‘void foo(T&&) [with T = int]’:
test.cpp:305:12: instantiated from here
test.cpp:299:2: error: call of overloaded ‘foo(int&)’ is ambiguous
I don't understand why this is ambiguous. The call to foo() within the R-value overload should clearly call the L-value version. So why doesn't this compile?
Short version: Try updating your compiler. Your version doesn't implement http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1164 .
Your second template is a "perfect forwarding" template. Any function template parameter of type T&&, where T is a template parameter will deduce that template parameter to X (where X is the argument type) when the argument is an rvalue, and to X& if the argument is an lvalue.
In your case you passed an rvalue, so T was deduced to Obj (and int in your real code). If you would have passed a variable name or something else that is an lvalue, your second template would have the parameter type Obj& (T would be Obj&, and && applied to such a type stays Obj&).
But the other template also has such a parameter type. So during overload resolution the conversion of the argument to the parameter is the same (perfect match), and another criteria needs to inspected, the specifity of the two templates, under the partial ordering rule. If one template is more specialized than the other template, then it will be chosen by the compiler. If there is no template more specialized than the other one, a final ambiguity is risen.
In this case, the first template is more specialized than the second template, and hence the compiler should call the first template finally.