When to supply default arguments as template arguments - c++

template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>){ //#1
}
int main(){
func(Test<int>{}); //#2
}
It seems to no rule in the standard that mentioned what situation the default argument is required for template-parameter.
In dcl.fct.default#1
If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.
In this section, the rule explicitly describe when to supply default arguments for a function call. However I haven't found a quote in the standard similar with the above sentence that describes when to supply default arguments as template arguments.
Such as Test<T> at #1. Maybe at #1, the specialization would be Test<T,T>, It's just inference. However, there's no formally terminology explicitly describe this in the standard.
The only quote that implies the default arguments as template arguments is in the following rule:
temp#arg-8
When a simple-template-id does not name a function, a default template-argument is implicitly instantiated when the value of that default argument is needed. [ Example:
template<typename T, typename U = int> struct S { };
S<bool>* p; // the type of p is S<bool, int>*
The default argument for U is instantiated to form the type S<bool, int>*.  — end example ]
Consider the code at #1, Does the default argument need at #1? If it is(It seems to It need at that point because If I don't specify a default argument for template parameter U, then at the point #1 will occur an error. see godbolt outcome), According to the above quote, the default template-argument need to be implicitly instantiated, However at this point, T is a template-parameter and the definition of such function template instantiate nothing(It's just a function template definition at this point). So, How does the quote interpret this?

There is also this paragraph inside [temp.names]:
A template-id is valid if
there is an argument for each non-deducible non-pack parameter that does not have a default template-argument,
So one could consider that a default-argument is needed means a default-argument is needed in order to make the template-id valid and that when there lacks a template argument, the argument is the default argument value. But I have not found anything explicit in the standard for class templates. For function template this is more explicit. Probably nobody pointed out this hole in the standard because this is a common pattern: default is used in place of what is not user provided. Maybe it will not be changed because the definition of default in the english dictionary is already given:
IT. the way that something will happen or appear automatically, especially on a computer, if you do not make any different choices
-- cambrige online dictionary

According to the above quote, the default template-argument need to be implicitly instantiated, However at this point, T is a template-parameter and the definition of such function template instantiate nothing(It's just a function template definition at this point). So, How does the quote interpret this?
The key is the difference between need of instantiation and when referenced. In the example from [temp.arg]/8, the default template argument for U is instantiated because it is needed for the instantiation of S<bool>, which unambiguously resolves to S<bool, int>. In the OP's own example, instantiation of the instantiation-dependent default template argument for U will only follow after overload resolution resolves void func(Test<T>) as the best viable overload and thus instantiates it. Just inspecting candidate overloads as part of overload resolution, however, will not lead to instantiation of any of the candidates nor any of the function template candidates parameters that are, themselves, templates. It will, however, be referencing said template parameters as part of the overload resolution process (discarding non-viable candidates).
From [temp.deduct]/1 [emphasis mine]:
When a function template specialization is referenced, all of the template arguments shall have values. The values can be explicitly specified or, in some cases, be deduced from the use or obtained from default template-arguments. [...]
For the OP's particular example, the former (instantiation) only occurs after overload resolution has finished and a best viable overload has been instantiated (which in turn instantiates the class template function argument). The latter (a function/class template specialization being referenced to), however, applies also during overload resolution, even when discarding candidates that are either not viable or not the best viable match, candidates that will never lead to instantiations.
From [temp.deduct]/2 [emphasis mine]:
When an explicit template argument list is specified, the template arguments must be compatible with the template parameter list and must result in a valid function type as described below; otherwise type deduction fails. Specifically, the following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:
(2.1) The specified template arguments must match the template parameters in kind (i.e., type, non-type, template). There must not be more arguments than there are parameters unless [...]
and from [temp.deduct]/6 [emphasis mine]:
At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
it is clear that as part of the "end of template argument deduction", any template arguments that have not been explicitly specified will be either deduced or obtained from default arguments and substituted into the candidate function before discarding or ranking it as a viable overload. This applies without any relation to instantiation, and only in the context of a given specialization (that may not yet or ever be instantiated) being referenced, as part of overload resolution.
Finally, from [temp.deduct.type]/3 [emphasis mine]:
A given type P can be composed from a number of other types, templates, and non-type values:
[...]
(3.3) A type that is a specialization of a class template (e.g., A<int>) includes the types, templates, and non-type values referenced by the template argument list of the specialization.
we note that as part of function template argument deduction for a function template parameter that is a specialization of a class template, the function template parameter type (used for deduction against an argument) includes the types referenced by the template argument list of the template parameter, meaning the particular class template specialization (function template argument) is referenced, so that, as per [temp.deduct]/1, it will go through template argument deduction and default-argument inspection for any of its template arguments that are not explicitly specified.

Related

Exact rules for matching variadic template template parameters in partial template specialization

While creating this answer for another question I came around the following issue. Consider this program (godbolt):
#include <variant>
#include <iostream>
template <typename T>
struct TypeChecker {
void operator()() {
std::cout << "I am other type\n";
}
};
template<typename... Ts, template<typename...> typename V>
requires std::same_as<V<Ts...>, std::variant<Ts...>>
struct TypeChecker<V<Ts...>>
{
void operator()()
{
std::cout << "I am std::variant\n";
}
};
int main()
{
TypeChecker<std::variant<int, float>>{}();
TypeChecker<int>{}();
}
The output (which is also expected) is the following (with clang 14.0.0 as well as with gcc 12.1):
I am std::variant
I am other type
If however the three dots in the parameter list of the template template are removed, like this (whole program live on godbolt):
template<typename... Ts, template<typename> typename V>
,then the output is different for clang and gcc. The clang 14.0.0 compiled program outputs
I am other type
I am other type
whereas the gcc 12.1 compiled program outputs
I am std::variant
I am other type
It seems that using the non-variadic template template exhibits different matching rules in clang and gcc. So my question is, which behavior is correct if it is even well defined, and why?
Since defect report resolution P0522R0 was adopted, exact matching of template parameter lists for template template parameter match is no longer needed, and correct output according to the standard is:
I am std::variant
I am other type
In the current draft (which also contains changes related to C++20 concepts) relevant standard excerpts are temp.arg.template#3-4 (bold emphasis mine):
A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A. In this comparison, if P is unconstrained, the constraints on A are not considered. If P contains a template parameter pack, then A also matches P if each of A's template parameters matches the corresponding template parameter in the template-head of P. Two template parameters match if they are of the same kind (type, non-type, template), for non-type template-parameters, their types are equivalent ([temp.over.link]), and for template template-parameters, each of their corresponding template-parameters matches, recursively. When P's template-head contains a template parameter pack ([temp.variadic]), the template parameter pack will match zero or more template parameters or template parameter packs in the template-head of A with the same type and form as the template parameter pack in P (ignoring whether those template parameters are template parameter packs).
A template template-parameter P is at least as specialized as a template template-argument A if, given the following rewrite to two function templates, the function template corresponding to P is at least as specialized as the function template corresponding to A according to the partial ordering rules for function templates. Given an invented class template X with the template-head of A (including default arguments and requires-clause, if any):
(4.1) Each of the two function templates has the same template parameters and requires-clause (if any), respectively, as P or A.
(4.2) Each function template has a single function parameter whose type is a specialization of X with template arguments corresponding to the template parameters from the respective function template where, for each template parameter PP in the template-head of the function template, a corresponding template argument AA is formed. If PP declares a template parameter pack, then AA is the pack expansion PP... ([temp.variadic]); otherwise, AA is the id-expression PP.
If the rewrite produces an invalid type, then P is not at least as specialized as A.
So, as we see, exact (up to special rules for parameter pack) parameter matching is now considered only in the case parameter list of template template parameter contains a pack (like your original example), otherwise only the new at least as specialized as relation is used to test matching, which defines for both template template parameter and argument respective function templates and tests whether parameter-induced function is at least as specialized as argument-induced function according to partial ordering rules for template functions.
In particular, matching A=std::variant to P=template<typename> typename V we get these corresponding function templates:
template<typename...> class X;
template<typename T> void f(X<T>); // for P
template<typename... Ts> void f(X<Ts...>); // for A
So, to prove A matches P, we need to prove f(X<T>) is at least as specialized as f(X<Ts...>). temp.func.order#2-4 says:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process. If both deductions succeed, the partial ordering selects the more constrained template (if one exists) as determined below.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. ...
Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].
Transformed template of f(X<T>) is f(X<U1>) and of f(X<Ts...>) is f(X<U2FromPack>), where U1 and U2FromPack are two synthesized unique types. Now, temp.deduct.partial#2-4,8,10 says:
Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type.
[Note 1: The creation of the transformed type is described in [temp.func.order]. — end note]
The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template.
The types used to determine the ordering depend on the context in which the partial ordering is done:
(3.1) In the context of a function call, the types used are those function parameter types for which the function call has arguments.130
(3.2) In the context of a call to a conversion function, the return types of the conversion function templates are used.
(3.3) In other contexts the function template's function type is used.
Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A.
Using the resulting types P and A, the deduction is then done as described in [temp.deduct.type]. ... 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.
Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.
Now, in our case, per 3.3, the function type itself is the only one considered among types to determine ordering. So, per 2, 8 and 10, to know whether f(X<T>) is at least as specialized as f(X<Ts...>) we need to see whether void(X<T>) is at least as specialized as void(X<Ts...>), or, equivalently, whether deduction from type for P=void(X<Ts...>) from A=void(X<U1>) succeeds. It does according to temp.deduct.type#9-10:
If P has a form that contains <T> or <i>, then each argument Pi
of the respective template argument list of P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi. ...
Similarly, if P has a form that contains (T), then each parameter type Pi of the respective parameter-type-list ([dcl.fct]) of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A. ...
Here, per 10, comparison of functions types results in a single comparison of X<Ts...> and X<U1>, which, according to 9, succeeds.
Thus, deduction is succesful, so f(X<T>) is indeed at least as specialized as f(X<Ts...>), so std::variant matches template<typename> typename V. Intuitively, we gave 'more general' template template argument which should work nicely for intended usage of a more specific template template parameter.
In practice, different compilers enable P0522R0 changes under different circumstances, and cppreference template parameters page (section Template template arguments) contains links and information on GCC, Cland and MSVC. In particular, GCC in C++17+ mode enables it by default (and for previous standards with compiler flag fnew-ttp-matching), but Clang doesn't in any mode unless -frelaxed-template-template-args flag is provided, thus you got the difference in output for them. With the flag, Clang also produces correct behaviour (godbolt).

MSVC not able to disambiguate between function templates when one of them contains a pack

Recently I reported a msvc bug involving a function parameter pack. Also as it turns out here that msvc is actually standard compliant there.
Then when I modified the example to what is shown below I noticed that the modified code also cannot be compiled in msvc but can be compiled in clang and gcc. The code is as follows: Demo link
template<typename T> struct C{};
template<typename T> void f(C<T>)
{
}
template<typename... T> void f(C<T...>)
{
}
int main()
{
f(C<int>{}); //Should this call succeed?
}
Note that in the above example we have as the function parameter C<T...> as opposed to just T.... Now, in the above shown example, I am not 100% sure that if it is a msvc issue or the standard disallows the program.
So my question is, is the above shown code example well formed. That is, should the call f(C<int>{}); succeed choosing the first overload void f(C<T>) instead of void f(C<T...>)?
In other words, which compiler is right here.
So my question is, is the above shown code example well formed.
The code is well-formed as the function template overload with a non-variadic template parameter is more specialized than the overload with a variadic template parameter, by partial ordering rules.
MSVC is incorrect to reject it.
[temp.func.order]/1 through /4 tells us that we need to turn to partial ordering
/1 If a function template is overloaded, the use of a function template
specialization might be ambiguous [...]. Partial ordering of overloaded function template declarations is used in the following contexts to select the function template to which a function template specialization refers:
/1.1 during overload resolution for a call to a function template specialization ([over.match.best]);
[...]
/2 Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. [...] If so, the more specialized template is the one chosen by the partial ordering process. If both deductions succeed, the partial ordering selects the more constrained template (if one exists) as determined below.
/3 To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs
thereof) synthesize a unique type, value, or class template
respectively and substitute it for each occurrence of that parameter
in the function type of the template.
/4 Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].
[temp.deduct.partial]
/2 Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type
and the transformed function type. The deduction process uses the
transformed type as the argument template and the original type of the
other template as the parameter template. [...]
/4 Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of
P and A.
/8 Using the resulting types P and A, the deduction is then done as described in [temp.deduct.type]. [...] 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.
results in, for the original single function parameter function templates, the following P/A pairs:
P A
-------- ----------
#1: C<T> C<Unique1...>
#2: C<T...> C<Unique2>
Typically, when not in the context of partial ordering, deduction would succeed for both these pairs. However, [temp.deduct.type]/9 has a special case for when the deduction is performed as part of partial ordering:
/9 [...] During partial ordering, if Ai was originally a pack expansion:
/9.2 otherwise, if Pi is not a pack expansion, template argument deduction fails.
This clause means deduction of #1 above (C<T> from C<Unique1...>) fails, whereas #2 (C<T...> from C<Unique2>) succeeds and C<Unique2> is considered at least as specialized as C<T...>. As per [temp.deduct.partial]/10 the non-variadic function template overload is thus at least as specialized as the variadic template function overload and, in the absense of the vice-versa relationsship, moreover more specialized:
/10 Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering,
the type from F is at least as specialized as the type from G. F is
more specialized than G if F is at least as specialized as G and G is
not at least as specialized as F.
This leads us back to [temp.func.order]/2 and the more specialized function template is chosen by partial ordering:
/2 [...] The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
Which is also covered by [over.match.best]/2.5
/2 Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments
i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
[...]
/2.5 F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2
according to the partial ordering rules described in
[temp.func.order], or, if not that, [...]

deducing function template specialization when taking address

I'm trying to understand the rules for function template argument deduction in the case where all arguments are defaulted. Under 13.10.1 (Explicit template argument specification), the standard (C++20) says:
— when the address of a function is taken, when a function initializes a reference to function, or when a pointer to member function is formed, ... If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted.
However, in the fourth line of the code snippets below, compilers (gcc, MSVC) seem to insist on having the empty angle brackets. Is this non-conformance or have I missed something ? (In this example, the issue doesn't matter, but the question arose in a context where it does matter).
template <typename T = int> void f(T) {}
void (*p)(int) = f; //ok
auto a = f<>; //ok
auto b = f; //error, can't deduce
void g() {}
auto c = g; //OK
The compilers are correct: auto a = f<>; is well-formed and auto b = f; is ill-formed. This is due to the rules about auto, combined with the rules about the address of the function template name f.
As described in [dcl.type.auto.deduct]/4, the auto type for the variable definitions is found in a way like template argument deduction done for a hypothetical function template using a template type parameter in its function parameter:
template <typename U> void auto_deducer(U);
auto a = f<>; // auto becomes the U deduced for auto_deducer(f<>)
auto b = f; // auto becomes the U deduced for auto_deducer(f)
In [temp.deduct.type] paragraphs 4 and 5:
In certain contexts, however, the value [of a template argument] 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.
The non-deduced contexts are:
...
A function parameter for which the associated argument is an overload set, and one or more of the following apply:
...
the overload set supplied as an argument contains one or more function templates.
...
So for both a and b, the template argument deduction used to determine the type of auto fails. Since it's not true that "all of the template arguments can be deduced", we're not allowed to omit the empty <> syntax.
But we're not entirely done yet, because the same paragraph [temp.arg.explicit]/4 you quoted has some relevant additional text:
Trailing template arguments that can be deduced or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack.... If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted. In contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
For the definition of both a and b, the f<> or f expression is in a non-deduced context, so "deduction is not done". (The template argument deduction for the hypothetical auto_deducer(f<>) then fails, but that's for the overall auto_deducer call, after the part actually involving f<> determined that the type deduction step should not be done with that argument at all.) In auto a = f<>;, "a template argument list is specified" and using the default template argument "identifies a single function template specialization" f<int>, so the paragraph's final sentence applies and the template-id names f<int> after all. In auto b = f; no template argument list is specified (and f is not a template-id), so the sentence can't apply.
An actual call like the statement f(); is fine because template argument deduction happens, uses the default template argument, and succeeds without the complication of auto. Converting expression f to a specific target pointer-to-function type is fine, because that will deduce the template parameter T from the target type, and the default template argument doesn't get involved.
Another confirmation this behavior is intended is found in a non-normative note in [over.over]/3. The [over.over] section applies when lookup for a name (like f or f<>) gives an overload set comprising one or more functions and/or function templates and the name expression is not followed by a function call argument list:
For each function template designated by the name, template argument deduction is done, and if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization, which is added to the set of selected functions considered. [ Note: As described in [temp.arg.explicit], if deduction fails and the function template name is followed by an explicit template argument list, the template-id is then examined to see whether it identifies a single function template specialization. If it does, the template-id is considered to be an lvalue for that function template specialization. The target type is not used in that determination. - end note ]
Trailing template arguments that can be deduced or obtained from default template-arguments
may be omitted from the list of explicit template-arguments. [...] If all of the template
arguments can be deduced, they may all be omitted; in this case, the empty template argument list <>
itself may also be omitted.
Note the last sentence, as opposed to the first sentence, does not say "deduced or obtained from default template-arguments". Deducing and obtaining from default template-arguments are two different things. This may or may not be a wording defect in the standard.

what is the detail of template argument deduction process

template<typename T>
struct Test{};
template<typename T>
struct Test<T&&>{};
Consider the above example, the standard says that the class template partial specialization shall be more specialized than its primary class template.
Within the argument list of a class template partial specialization, the following restrictions apply:
The specialization shall be more specialized than the primary template.
To determine which is more specialized, the following rules will be applied to them:
For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates:
Each of the two function templates has the same template parameters as the corresponding partial specialization.
Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.
For primary class template, the rewritten function template would be like this:
template<typename T>
void ordering(Test<T>)
And the rewritten function template for class template partial specialization would be like this:
template<typename T>
void ordering(Test<T&&>)
According to the rule for "Deducing template arguments during partial ordering":
The types used to determine the ordering depend on the context in which the partial ordering is done:
In the context of a function call, the types used are those function parameter types for which the function call has arguments.
In the context of a call to a conversion function, the return types of the conversion function templates are used.
In other contexts the function template's function type is used.
Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A. If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.
Neither function call nor call to a conversion function is this context. So the bullet 3 works. That means, Take void(Test<T>) as P and Take void(Test<T&&>) as A, vice versa.
For this pair P/A, it is the case mentioned in temp.deduct.type#10, that is,
each parameter type Pi of the respective parameter-type-list ([dcl.fct]) of P is compared with the corresponding parameter type Ai of the corresponding parameter-type-list of A.
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.
Here, we only have one parameter in each function type. So compare Test<T> with Test<T&&> and vice versa, such process are mentioned in temp.deduct.type#9.
However I argue here is that there's no relevant rule in the standard which says what's the detail about the comparison process. In other words, why we can deduce T from T&&(T would be T&&), But not the other way around. If I miss the relevant rules about the detail, please point it out. If there are indeed no such detail descriptions in the standard, where can I find the relevant technology about the detail of template argument deduction process?
It looks to me like what you've cited is a problem in what you're looking at, but it's already been noticed and fixed. In particular, you quote the following:
Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A. If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.
By N4800 (and maybe before--I haven't tracked down exactly when this was changed), this has been changed to the following (§[temp.deduct.partial]/4, 5):
4 Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A.
5 Before the partial ordering is done, certain transformations are performed on the types used for partial ordering:
(5.1) — If P is a reference type, P is replaced by the type referred to.
(5.2) — If A is a reference type, A is replaced by the type referred to.
This effectively removes the possibility of deducing T as a reference type, because the type from which it is deduced can never be a reference.
In [temp.deduct.call] and [temp.deduct.conv], it mentions that
In general, the deduction process attempts to find template argument
values that will make the deduced A identical to A.
I believe this rule is common knowledge so it is not mentioned in other sub-clauses (it is mentioned in the above two sub-clauses because there are exceptions in that two sub-clauses).

How does the template argument deduction perform for function template parameter when it is a class template with default argument

template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>){ //#1
}
int main(){
func(Test<int>{}); //#2
}
Consider the above code, At the point of invocation of function template func, the type of argument is Test<int,int>, When call the function template, template argument deduction will perform.
The rule of template argument deduction for function call is :
temp.deduct#call-1
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below.
I'm pretty sure the type of A is Test<int,int>, however I'm not sure what the type of P here is. Is it Test<T> or Test<T,T>, According to the rule, It seems to the type of P here is Test<T>, then deduction process is performed to determine the value of T that participate in template argument deduction. Then according to these rules described as the following:
temp.deduct#call-4
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).
temp.deduct#5
When all template arguments have been deduced or obtained from default template arguments, all uses of template parameters in the template parameter list of the template and the function type are replaced with the corresponding deduced or default argument values.
Because the class template Test has a default argument, hence the deduced T is substituted into default argument. That means the deduced A is Test<int,int> and it is identical to Argument type Test<int,int>.
However, It's just my understanding. I'm not sure what type the P here is. If change the type of function argument to Test<int,double>, the outcome will report:
candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')
The outcome looks like as if the P is Test<T,T> and the fist value of T is conflicting with the second value of T.
So, My question is:
Whether the P here is Test<T> or Test<T,T>? and why?
not a language lawyer answer
There is no type Test<T> is actually a "shorthand" for Test<T, T>.
Just like with default function arguments if you have int foo(int a, int b = 24) the type of the function is int (int, int) and any call like foo(11) is actually foo(11, 24).
P must be a type not a template. test <T> is a template-id, but it is not explicitly said in the standard that the template-id test <T> is equivalent to test<T,T>. The only thing that is said is:
A template-id is valid if
[...]
there is an argument for each non-deducible non-pack parameter that does not have a default template-argument, [...]
After that, holes in the standard are filled by our intuition oriented by the use of the term default.
I think the key point here is that a template designate a family, and a template-id cannot designate a family.
Whether the P here is Test<T> or Test<T,T>? and why?
P is Test<T,T>.
I think we can agree that the rules of [temp.deduct] applies also for class templates; e.g. [temp.class.order], covering partial ordering of class template specializations, is entirely based on the concept of re-writing the class templates to (invented) function templates and applying the rules of function templates to that of the invented function templates corresponding to the original class templates under partial ordering analysis. Combined with the fact that the standard passage for class templates is quite brief in comparison to function templates, I interpret the references below as applying also for class templates.
Now, from [temp.deduct]/1 [emphasis mine]:
When a function template specialization is referenced, all of the template arguments shall have values. The values can be explicitly specified or, in some cases, be deduced from the use or obtained from default template-arguments. [...]
and, from [temp.deduct]/2 [emphasis mine]:
When an explicit template argument list is specified, the template arguments must be compatible with the template parameter list and must result in a valid function type as described below; otherwise type deduction fails. Specifically, the following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:
(2.1) The specified template arguments must match the template parameters in kind (i.e., type, non-type, template). There must not be more arguments than there are parameters unless [...]
With extra emphasis on "is referenced" and "the specified template arguments"; there is no requirement that we specify all arguments for a given matching function(/class) template, only that those that do specify follow the requirements of [temp.deduct]/2 for explicitly specified template arguments.
This leads us to back to [temp.deduct]/1 for the remaining template arguments of a given candidate function/class template: these can be either deduced (function templates) or obtained from the default template arguments. Thus, the call:
func(Test<int>{});
is, as per the argument above, semantically equivalent to
func(Test<int, int>{});
with the main difference that the template arguments for the former is decided by both an explicitly specified template arguments and a default template argument, whereas for the latter both are decided by explicitly specified template arguments. From this, it is clear that A is Test<int, int>, but we will use a similar argument for P.
From [temp.deduct.type]/3 [emphasis mine]:
A given type P can be composed from a number of other types, templates, and non-type values:
[...]
(3.3) A type that is a specialization of a class template (e.g., A<int>) includes the types, templates, and non-type values referenced by the template argument list of the specialization.
Notice that the description in [temp.deduct.type]/3.3 now returns to the template argument list of the template type P. It doesn't matter that P, for when inspecting this particular candidate function in overload resolution, refers to a class template by partly explicitly specifying the template argument list and partly relying on a default template parameter, where the latter is instantiation-dependent. This step of overload resolution does not imply any kind of instantiation, only inspection of candidates. Thus, the same rules as we just applied to the template argument A above applies to P, in this case, and as Test<int, int> is referenced (via Test<int>), P is Test<int, int>, and we have a perfect match for P and A (for the single parameter-argument pair P and A of this example)
Compiler error messages?
Based in the argument above, one could arguably expect a similar error message for the OP's failing example:
// (Ex1)
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>) {}
int main() {
func(Test<int, double>{});
}
as for the following simple one:
// (Ex2)
struct Foo {};
template<typename T> struct Test {};
template<typename T> void f(T) {}
int main() {
f<Test<int>>(Test<Foo>{});
}
This is not the case, however, as the former yields the following error messages for GCC and Clang, respectively:
// (Ex1)
// GCC
error: no matching function for call to 'func(Test<int, double>)'
note: template argument deduction/substitution failed:
deduced conflicting types for parameter 'T' ('int' and 'double')
// Clang
error: no matching function for call to 'func'
note: candidate template ignored: deduced
conflicting types for parameter 'T' ('int' vs. 'double')
whereas the latter yields the following error messages for GCC and Clang, respectively:
// (Ex2)
// GCC
error: could not convert 'Test<Foo>{}' from 'Test<Foo>' to 'Test<int>'
// Clang
error: no matching function for call to 'f'
note: candidate function template not viable:
no known conversion from 'Test<Foo>' to 'Test<int>' for 1st argument
We can finally note that if we tweak (Ex1) into explicitly specifying the single template argument of f, both GCC and Clang yields similar error messages as for (Ex2), hinting that argument deduction has been entirely removed from the equation.
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>) {}
int main() {
func<int>(Test<int, double>{});
}
The key for this difference may be as specified in [temp.deduct]/6 [emphasis mine]:
At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
namely that the template argument deduction process is separated into a clear beginning and end, categorizing:
explicitly specified template arguments as the beginning of the process, and,
deduced or default argument-obtained template arguments as the end of the process,
which would explain the differences in the error messages of the examples above; if all template arguments have been explicitly specified in the beginning of the deduction process, the remainder of the process will not have any remaining template argument to work with w.r.t. deduction or default template arguments.
I tryed to come up with a code that forces only class deduction without function deduction.
Here, there are no function instantiations, but the compiler emits an error anyway:
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T, T>){
}
template<typename T>
void func(Test<T>){
}
redefinition of 'template<class T> void func(Test<T, T>)'
GCC: https://godbolt.org/z/7c981E
Clang:
https://godbolt.org/z/G1eKTx
Previous wrong answer:
P refers to template parameter, not to template itself. In declaration Test<typename T, typename U = T> P refers to T, not to Test. So in the instantiation Test<int> T is int, just like A in the call is also int.