Auto detection of template parameters without arguments - c++

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.

Related

Passing a lambda argument to a std::function parameter without intermediate variable

This might look similar to "I cannot pass lambda as std::function", but I'm actually passing the std::function parameter by value, so that problem doesn't apply. I've defined the following function.
template<typename T>
std::vector<T> countSort(const std::vector<T> &v, std::function<int(T)> keyFunc, int n);
The second parameter is an std::function that maps T to int (passed by value).
When calling this, I wanted to use a lambda expression, as follows:
std::vector<int> v;
[...]
v = countSort(v, [](int x) { return x; }, 10);
But the template argument deduction fails, because "main()::<lambda(int)> is not derived from std::function<int(T)>". It does work if I specify the template argument, or if I introduce an intermediate variable of type std::function for the lambda expression:
std::function<int(int)> lambda = [](int x) { return x; };
v = countSort(v, lambda, 10);
Why can't I do the former? I'm giving the compiler the exact same information; if it is able to convert a value of type lambda<int> to std::function<int(int)> when assigning it to a variable, why can't it directly convert from lambda<int> to the parameter type, which is std::function<T(int)>—and taking into account that v is of type std::vector<int>, it should know that T is int? The whole reason I want to use a lambda expression is precisely that, it's an expression, so I should be able to write it inline in the function call argument list, without having to give it a name or assign it to a variable.
The problem is, template argument deduction doesn't consider implicit conversion (from lambda to std::function), which causes the deduction for T on the 2nd function parameter keyFunc to fail.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
You can use std::type_identity (since C++20) to exclude the 2nd function parameter from deduction. e.g.
template<typename T>
std::vector<T> countSort(const std::vector<T> &v, std::function<int(std::type_identity_t<T>)> keyFunc, int n);
BTW: If your compiler doesn't support std::type_identity, it's not hard to make one.
And about how std::type_identity works here, see non-deduced context:
(emphasis mine)
In the following cases, the types, templates, and non-type values that
are used to compose P do not participate in template argument
deduction, but instead use the 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.
The nested-name-specifier (everything to the left of the scope
resolution operator ::) of a type that was specified using a
qualified-id:

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…

Partial ordering on T*... and const T&

cppreference claims the following
template <class ...T> int f(T*...); // #1
template <class T> int f(const T&); // #2
f((int*)0); // OK: selects #1
// (was ambiguous before DR1395 because deduction failed in both directions)
If we follow DR1395 we see
If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails. Otherwise, using Using the resulting types P and A, the deduction is then done as described in 17.9.2.5 [temp.deduct.type]. If P is a function parameter pack, the type A of each remaining parameter type of the argument template is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template. If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.
[...]
If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing paramter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.
From what I can infer, this means we should be matching each individual type expanded from T*... to const T& and vice versa. In this case, T* is more specialized than const T& (T from U* succeeds, T* from U fails).
However, the compilers disagree. Clang thinks it's ambiguous and gcc thinks the second should be called, both of which differs from cppreference.
What is the correct behaviour?
cppreference is correct for this example (which is exactly the example from the CWG issue, as well as CWG 1825). Let's go through the deduction both ways.
Deduce template <class ...T> int f(T*...); from const U&. This fails, not going to be able to deduce T* from const U& - the fact that it's a pack here is immaterial. So #2 is not at least as specialized as #1.
Deduce template <class T> int f(const T&); from U*... We used to have the rule "If A was transformed from a function parameter pack and P is not a parameter pack, type deduction fails." - which would have meant that we failed before we even tried to do anything else. But CWG 1395 removed that sentence, so we keep going, and we have the new sentence:
Similarly, if A was transformed from a function parameter pack, it is compared with each remaining parameter type of the parameter template.
We compare U*, by itself, to each remaining parameter type, which is const T&. That deduction succeeds. So #1 is at least as specialized as #2.
As a result, now #1 is more specialized than #2. The quote you cite about trailing parameter packs as a later tiebreaker doesn't apply - since we don't have the case where each function template is at least as specialized as the other. Also the other quote you cite ([temp.deduct.type]/10 is about deducing function types, so I don't think it applies here either? Although I'm also not sure about the example in that section - or what that particular rule actually means.

Is this correct behavior of template?

template<class blah, class bleh>
blah func(bleh p)
{
// Do something
}
int main()
{
double d=1.111;
int i = func<int>(d); // #1
int j = func<int,double>(d); // #2
// ....
}
In this example both the instances of func, #1 and #2 are compiling, but I'm unsure of what is correct, and why.
Can some one explain why #1 is correct, and maybe give some background?
Yes, this is correct behaviour.
Case 1 - type deduction
func<int>(d);
This uses template type deduction to determine the type for bleh.
In order to instantiate a function template, every template argument must be known, but not every template argument has to be specified. When possible, the compiler will deduce the missing template arguments from the function arguments. This occurs when a function call is attempted and when an address of a function template is taken.
The compiler sees the type for d as being a double and thus deduces the actual type for bleh must also be a double.
From cppreference, also covered in § 14.8.2 of the C++ specification;
Template argument deduction attempts to determine template arguments ..., which can be substituted into each parameter P to produce the type deduced A, which is the same as the type of the argument A, ... .
If there are multiple parameters, each P/A pair is deduced separately and the deduced template arguments are then combined. If deduction fails or is ambiguous for any P/A pair or if different pairs yield different deduced template arguments, or if any template argument remains neither deduced nor explicitly specified, compilation fails.
Case 2
func<int,double>(d);
The type for bleh is explicitly set to double, hence the compiler will make it such. The argument d is provided and since it is also a double, the compiler happily continues. If an argument (i.e. in place of d) was provided with a type that was not a double, or could not implicitly be converted to a double (e.g. via promotions, non-explicit constructors or user provided conversions), this would result in an error.

Is there a way to deduce the value of a function pointer template parameter?

C++ allows non-type template parameters to be of pointer, including function pointer, type. I recently asked a question about what this is useful for, and this is a follow up to one of the answers.
Is it posible to deduce the value of a function pointer template parameter, from a function argument that is the function pointer in question? For example:
using VoidFunction = void(*)();
template <VoidFunction F>
void templ(VoidFunction);
...
void func(); // a VoidFunction
...
templ<func>(func); // works, but I have to specify the template parameter explicitly
templ(func); // <-- I would like to be able to do this
Is there a way to get this deduction to happen? It seems technically possible from a compiler implementer's point of view, as long as the function argument can be resolved to a function in the code at compile time.
If you're wondering about the motivation behind this, see the comments under this answer, particularly a possible optimization for the implementation of std::bind().
EDIT: I realize that I could simply remove the function argument and use the template argument, as in templ<func>(). My only purpose of adding in the function argument was to try to avoid having to pass the template argument.
I guess what I really want, is to also deduce the type of the function pointer, as in:
template <typename Function, Function F>
void templ(/* something */);
and then be able to call
templ(func);
or
templ<func>();
and have both the type and value be deduced from a single mention of the function pointer.
Hope that makes more sense now.
Template arguments for a function are deduced from the types of the function's template parameters. Template arguments can only be deduced from a type when that type is one of the allowed forms. The allowed forms are specified in [temp.deduct.type]
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.
A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
T
cv-list T
T*
T&
T[integer-constant]
template-name (where template-name refers to a class template)
type(*)(T)
T(*)()
T(*)(T)
T type::*
type T::*
T T::*
T (type::*)()
type (T::*)()
type (type::*)(T)
type (T::*)(T)
T (type::*)(T)
T (T::*)()
T (T::*)(T)
type[i]
template-name&lti> (where template-name refers to a class template)
TT<T>
TT<i>
TT<>
where (T) represents argument lists where at least one argument type contains a T, and () represents argument lists where no parameter contains a T. Similarly, <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i.
When considering only non-type template arguments, the relevant forms are those that contain i:
type[i]
template-name&lti> (where template-name refers to a class template)
TT<i>
Therefore it is not possible to deduce the value directly from the value of a function argument that is the function pointer. However it is possible to deduce the value of a non-type template argument if the function parameter has one of the specified forms.
The following code ahieves this by wrapping the non-type template argument value in a class-template called NonType. The parameter of f is in the form template-name<i>, making it possible for the value of its non-type template argument to be deduced.
template<typename T, T value>
struct NonType {};
template<typename T, T value>
void f(NonType<T, value>)
{
}
void g();
struct A
{
void f();
int m;
};
int i;
#define MAKE_NONTYPE(value) NonType<decltype(value), (value)>()
int main()
{
f(MAKE_NONTYPE(0)); // NonType<int, 0>
f(MAKE_NONTYPE(&g)); // NonType<void(*)(), &g>
f(MAKE_NONTYPE(&A::f)); // NonType<void(A::*)(), &A::f>
f(MAKE_NONTYPE(&A::m)); // NonType<int A::*, &A::m>
f(MAKE_NONTYPE(&i)); // NonType<int*, &i>
}
Note that decltype and the MAKE_NON_TYPE macro are used here only as a convenience, to avoid having to write out the full template argument list of NonType