Seemingly defective paragraph regarding template argument deduction? - c++

Omitting a template argument is possible, since the standard says in 17.9.1, paragraph 3 [temp.arg.explicit] (N4700):
Trailing template arguments that can be deduced or obtained from default template-arguments may be omitted from the list of explicit template-arguments...
On the other hand, the standard has a conflicting rule, as 17.9.2, paragraph 2 [temp.deduct] shows (emphasis mine):
... Specifically, the following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:
If the specified template arguments do not match the template parameters in kind (i.e., type, non-type, template), or if there are more arguments than there are parameters and no parameter is a template parameter pack, or if there is not an argument for each non-pack parameter, type deduction fails.
...
This paragraph suggests that all template arguments shall be specified if we were to use a explicit template argument list, which clearly contradicts to the above paragraph. Then, what's the intention?

The second quote applies "when evaluating an explicitly specified template argument list", but the first clearly is about the case when some template arguments are not explicitly specified. There's no conflict, because they're talking about two different situations.

Related

What normative rules govern and allow SFINAE?

SFINAE, "Substitutation Failure Is Not An Error", is a well-known rule/technique applied during overload resolution of function templates (see e.g. SFINAE # cppreference). However, it is never referred to by that name in the C++ standard; the only reference I can find is that of an implementation-dependent macro from <version>, as specified in [version.syn]/2
#define __cpp_­lib_­result_­of_­sfinae 201210L // also in <functional>, <type_­traits>
What is(/are) the normative reference(s) for this rule/technique?
Let's start from an unqualified function call (f(1) below), particularly in an example constructed such that the postfix-expression of the function call names two function templates, one of which we expect to be rejected by SFINAE.
#include <type_traits>
// A: expected to be viable
template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
constexpr bool f(T) { return true; }
// B: expected to be rejected
template<typename T, std::enable_if_t<!std::is_integral_v<T>>* = nullptr>
constexpr bool f(T) { return false; }
static_assert(f(1));
The function call above will, as per [over.match.call.general]/1, result in overload resolution applied as specified in [over.call.func], particularly [over.call.func]/3:
[over.match.call.general]/1 In a function call
postfix-expression ( expression-list_opt )
if the postfix-expression names at least one function or function template, overload resolution is applied as specified in [over.call.func].
[over.call.func]/3 [...] The name is looked up in the context of the function call following the normal rules for name lookup in expressions ([basic.lookup]). The function declarations found by that lookup constitute the set of candidate functions. [...]
[over.match.general]/2 decribes the high-level steps of overload resolution:
Overload resolution selects the function to call in seven distinct contexts within the language:
(2.1) invocation of a function named in the function call syntax;
[...]
Each of these contexts defines the set of candidate functions and the list of arguments in its own unique way. But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:
[...]
Telling us that selecting a function during overload resolution works in three different phases:
Identify the candidate functions and associated argument lists
From these, select the subset of viable functions
From these, select the best viable function based on ranking via implicit conversion sequences matching arguments with parameters
SFINAE works in the domain of 1 whilst identifying candidate functions, not so much by rejecting potential candidates but rather by failing to generate a potential candidate when such a potential candidate is a function template (specialization), particularly failing due to template deduction failures. Our example is in the context of invocation of a function named in the function call syntax, but for the context of finding the normative rules that governs SFINAE, it is representable also for other contexts as listed by [over.match.general]/2.
[over.match.funcs], particularly [over.match.funcs.general]/7 tells us that we enter the domain of template argument deduction for candidates that are function templates:
In each case where a candidate is a function template, candidate function template specializations are generated using template argument deduction ([temp.over], [temp.deduct]) [...] Those candidates are then handled as candidate functions in the usual way.
[temp.over] covers overload resolution rules specific to function templates and [temp.over]/1, in particular, contains one of two essential rules which govern SFINAE:
When a call to the name of a function or function template is written [...] template argument deduction [...] [is] performed [...] if the argument deduction and checking succeeds [...] [the] function template specialization [...] is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template.
Namely that a template argument deduction failure in the phase of finding candidate functions during overload resolution results in the function not being included in the set of candidates.
[temp.deduct] covers template argument deduction, and includes several ways in which it can fail, e.g. [temp.deduct]/2, /5, and /7, but the second essential rule which governs SFINAE is particularly found in [temp.deduct]/8:
/7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. [...] The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. [...]
/8 If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments.
[...]
Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.
The latter paragraph of [temp.deduct]/8 is the reason for some notoriously tricky non-SFINAE hard failures when using generic lambdas in return type deduction. See e.g. P0238R1 for details.
If we return to the function call f(1) in the example above, the function template B will be rejected as a candidate as template argument deduction fails when replacing T in std::enable_if_t<!std::is_integral_v<T>>*, namely in the 2nd type template parameter of the template-head, with substitution of the deduced T (int) resulting in an invalid type (std::enable_if_t<!std::is_integral_v<int>>*).
[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. If the substitution results in an invalid type, as described above, type deduction fails.
Finally, [temp.deduct]/5 is also where we find rules for constraints, which are not a SFINAE mechanism, but a separate step where non-satisfaction results in type deduction failure.
/5 [...] If the function template has associated constraints ([temp.constr.decl]), those constraints are checked for satisfaction ([temp.constr.constr]). If the constraints are not satisfied, type deduction fails.

What kind of type-specifier within decl-specifier-seq grammar tree can denote the type-specifier when it's a template parameter pack

template<typename...T>
void func(T...){
}
Consider the above code , it's a case what the standard mentioned:
There is a syntactic ambiguity when an ellipsis occurs at the end of a parameter-declaration-clause without a preceding comma. In this case, the ellipsis is parsed as part of the abstract-declarator if the type of the parameter either names a template parameter pack that has not been expanded or contains auto; otherwise, it is parsed as part of the parameter-declaration-clause.
So, the parameter-declaration is the form of the following:
dcl.fct#3
parameter-declaration:
attribute-specifier-seq(opt) decl-specifier-seq abstract-declarator(opt)
Thereof, the abstract-declarator denotes ..., However, I couldn't find a type-specifer within decl-specifier-seq's grammar tree can denote T.
dcl.spec#1
decl-specifier:
storage-class-specifier
defining-type-specifier
function-specifier
friend
typedef
constexpr
inline
decl-specifier-seq:
decl-specifier attribute-specifier-seq (opt)
decl-specifier decl-specifier-seq
Because T here is an unexpanded template parameter type. So T is not typedef-name, because of this:
temp.param#3
A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name (if declared without template) or template-name (if declared with template) in the scope of the template declaration. [ Note: A template argument may be a class template or alias template.
So, I wonder what something within the decl-specifier-seq grammar tree can denote the template parameter pack T which has not been expanded? As far as now, I have not found a type-specifier associated with T. If I miss anything, Please correct me.
UPDATE
For this question, I'm digging in the further into temp.variadic#5 section, and I found something that says:
For the purpose of determining whether a parameter pack satisfies a rule regarding entities other than parameter packs, the parameter pack is considered to be the entity that would result from an instantiation of the pattern in which it appears.
My reading about this sentence is, the parameter pack is required to satisfy the rule about a parameter-declaration, hence when invoke the function like func(0,0.1), the instantiation of the pattern of the pack expansion are int,double, respectively, due to:
temp.variadic#4
A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below)
In a function parameter pack ([dcl.fct]); the pattern is the parameter-declaration without the ellipsis.
So, the results from these instantiations are used to be the type-specifier. It means int,double will be the type-specifier for these parameter-declarations of this case func(0,0.1), However it's my reading, I'm not sure it is a relevant rule for this questions.
And it will result in another question, for template<typename...T> void func(T...);, the parameter pack is not a function parameter pack, due to this rule:
A declarator-id or abstract-declarator containing an ellipsis shall only be used in a parameter-declaration. Such a parameter-declaration is a parameter pack. When it is part of a parameter-declaration-clause, the parameter pack is a function parameter pack. [ Note: Otherwise, the parameter-declaration is part of a template-parameter-list and the parameter pack is a template parameter pack; see [temp.param].  — end note ]
In my question, the parameter pack is parsed as a part of the abstract-declarator, not the part of a parameter-declaration-clause because the first quote said that. hence, the parameter pack is not considered to be a function parameter pack at all? Now, I think this question has became more complex. I'm looking forward to an answers for this question.

Deduction failure of function call with explicit template argument list and [temp.arg.explicit]/3

[temp.arg.explicit]/3 of the C++17 standard (final draft) says about deduction of function template arguments with explicitly specified template argument lists:
In contexts where deduction is done and fails, or [...], 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.
How does this apply to parameter packs?
Consider
template<typename...>
struct S {
S(int) {}
};
template<typename... A>
void f(S<A...>) {}
int main() {
f<int>(0);
}
This compiles on MSVC, but not on GCC and Clang, see godbolt. It would also be my intuition that it should fail, because deduction will fail, but the quote above seems to imply that even if deduction fails, since f<int> (in my understanding) identifies uniquely a template specialization, f<int> should be considered to refer to that specialization and then call it, without overload resolution, which will work, implicitly converting 0 to S<int>.
What is wrong in my understanding of the quote or is MSVC indeed correct?
Note that if we try to call f<>(0); (which I guess should work by the considerations above) all three compilers refuse to compile.
Relevant for the question is also [temp.arg.explicit]/6 that tells us that implicit conversions on a function parameter (as you want above) are
if the parameter type contains no template-parameters that participate in template argument deduction. [ Note: Template parameters do not participate in template argument deduction if they are explicitly specified. [...] ]
So, now is the question if A... participates in template argument deduction. (At this point I wanto to note that OPs code compiles also under gcc/clang if we replace the parameter pack by one template parameter, as it should since it is explicitly specified).
One could argue that A... is explicitly specified and therefore does not participate in deduction. But I would argue that one is wrong. [temp.arg.explicit]/9 tells us that deduction can extend explicitly specified template argument lists. Hence, f<int>(S<int, char>{0}); is valid and A... is deduced to int, char. So in this case A... definitely participates in deduction. But since this call only differs from your call by the parameter, the deduction also has to take place in your call, too.
In other words f<int>(0); could also mean to call f<int, char> and as such, it does not specify a single function template specification.
It's irrelevant. There's no such thing as a function call "without overload resolution". CWG2092 makes this clear.
[temp.over]/1 controls (broken apart for readability; emphasis mine):
When a call to the name of a function or function template is written
(explicitly, or implicitly using the operator notation), template
argument deduction ([temp.deduct]) and checking of any explicit
template arguments ([temp.arg]) are performed for each function
template to find the template argument values (if any) that can be
used with that function template to instantiate a function template
specialization that can be invoked with the call arguments.
For each function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to
synthesize the declaration of a single function template
specialization which is added to the candidate functions set to be
used in overload resolution.
If, for a given function template,
argument deduction fails or the synthesized function template
specialization would be ill-formed, no such function is added to the
set of candidate functions for that template. The complete set of
candidate functions includes all the synthesized declarations and all
of the non-template overloaded functions of the same name. The
synthesized declarations are treated like any other functions in the
remainder of overload resolution, except as explicitly noted in
[over.match.best].
There is an open core language issue (issue 2055: Explicitly-specified non-deduced parameter packs) that is well related to this case.
From my understanding of what is implictly stated in this issue, the intent is that compilers should behave as MSVC but the standard is said not to be clear enough.

Contexts in which pack expansions can occur

There is a clear and comprehensible list of contexts in which a pack expansion can occur on cppreference.com. What I am trying to do is to derive the same information from the standard, partly to gain experience in using the standard. However, I am unable to derive all pack expansion contexts listed on cppreference.com from the standard.
cppreference.com lists, among others, the following four contexts: function argument lists, template argument lists, function parameter lists and template parameter lists.
On the other hand, the standard says (14.5.3.4):
A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list (described below). The form of the pattern depends on the context in which the expansion occurs. Pack expansions can occur in the following contexts:
In a function parameter pack (8.3.5); the pattern is the parameter-declaration without the ellipsis.
In a template parameter pack that is a pack expansion (14.1):
if the template parameter pack is a parameter-declaration; the pattern is the parameter-declaration without the ellipsis;
if the template parameter pack is a type-parameter with a template-parameter-list; the pattern is the corresponding type-parameter without the ellipsis.
...
In a template-argument-list (14.3); the pattern is a template-argument.
...
I am unable to find out where the standard says that pack expansion can occur in function argument lists. I suppose this context is somehow covered by one of the three bullet points quoted above.
Just in case it is unclear what I mean by pack expansions in function argument lists, consider the following example:
template <typename ...Args> void f(Args ...args) {}
template <typename ...Args> void g(Args ...args) {
f(args...); // Pack expansion in a function argument list.
}
A function argument list is syntactically an initializer-list1, so it's covered by [temp.variadic]/(4.4):
In an initializer-list; the pattern is an initializer-clause.
1. The grammar of a function call is postfix-expression ( expression-listopt ). And an expression-list is just an initializer-list. ([expr.post])

Is there any guarantee on the order of substitution in a function template after type deduction?

Consider this function template:
template<typename T>
typename soft_error<T>::type foo(T, typename hard_error<T>::type)
{ }
After deducing type T from the type of the first argument in the call to foo(), the compiler will proceed to substitute T and instantiate the function signature.
If substitution for the return type gets executed first, causing a simple substitution failure, the compiler will discard this function template when computing the overload set and search for other viable overloads (SFINAE).
On the other hand, if substitution for the second function parameter occurs first, causing a hard error (e.g. because of a substitution failure in a non-immediate context), the entire compilation would fail.
QUESTION: Is there any guarantee on the order in which substitution will be performed for the function parameters and return types?
NOTE: This example seems to show that on all major compilers (VC11 was tested separately and gave identical results) substitution for the return type occurs before substitution for the parameter types.
[NOTE: This was not originally meant to be a self-answered question, but I happened to find out the solution while crafting the question]
Is there any guarantee on the order in which substitution will be performed for the function parameters and return types?
Not in the current standard.
However, this Defect Report (courtesy of Xeo) shows that this is indeed intended to be the case. Here is the proposed new wording for Paragraph 14.8.2/7 of the C++11 Standard (which has become part of the n3485 draft):
The substitution occurs in all types and expressions that are used in the function type and in template
parameter declarations. The expressions include not only constant expressions such as those that appear in
array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions)
inside sizeof, decltype, and other contexts that allow non-constant expressions. The substitution proceeds
in lexical order and stops when a condition that causes deduction to fail is encountered. [...]
As correctly pointed out by Nicol Bolas in the comments to the question, lexical order means that a trailing return type would be substituted after the parameter types, as shown in this live example.