Are variadic arguments after a defaulted parameter well-formed? - c++

template <typename... Args>
void bark( int = 0, Args&&... args ) {}
int main() {
bark();
bark(1);
bark(1, 2);
}
Is this code well-formed according to the C++ Standard?
The proposed duplicate does not contain the same calls of the function.

Due to CWG 777, the declaration is valid:
In a given function declaration, all each parameters subsequent to a
parameter with a default argument shall have a default arguments
supplied in this or a previous declarations or shall be a function
parameter pack.
Deduction should succeed in all three cases, since the default argument makes no difference to the nature of deduction: If no argument to the pack parameter args is provided, it's deduced to the empty pack via [temp.arg.explicit]/3, otherwise the usual rule in [temp.deduct.call]/1 applies (as the pack is clearly not in a non-deduced context).

Related

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.

Misunderstanding about non-deducible function template arguments

From C++ Templates - The Complete Guide 2nd edition:
Moreover, such parameters can't usefully be placed after a template parameter pack or appear in a partial specialization, because there would be no way to explicitly specify or deduce them.
template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps); // useless declaration because N
// cannot be specified or deduced
where such parameters refers (I think) to the template parameters corresponding to those template arguments that can never be deduced. I.e. in the example above N is the parameter that cannot be deduced because N+1 is "too complicated to be deduced".
But why specifying it is not possible? I understand that it's not possible to specify N and let ...Ts be deduced, but why isn't it possible to specify them all? In other words, what is wrong in specifying Ts=[int] and N=2 via the following?
double x[3];
f<int,2>(x,1);
I.e. in the example above N is the parameter that cannot be deduced because N+1 is "too complicated to be deduced".
Formally, this is [temp.deduct.type]/5.3
The non-deduced contexts are:
[...]
/5.3 A non-type template argument or an array bound in which a subexpression references a template parameter.
As is already covered in the following Q&A:
Which part of the C++ standard prevents explicitly specifying this template's arguments?
particularly that in a template-head of a function template
template<typename ...Ts, int N>
// ... function template
as per [temp.param]/14
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]).
specifically as per the special rule for function templates (due to function template argument deduction), the template argument N must be deducible from the function's parameter list. As per [temp.deduct.type]/5.3, it is not, and f in the following example can never invoked (overload resolution will never consider it a viable candidate):
template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps);
whereas the following functions can both be found by overload resolution:
template<typename ...Ts, int N>
void g(double (&)[N], Ts ... ps); // N deducible from function parameter
template<typename ...Ts, int N = 2> // N has a default-template-argument
void h(double (&)[N+1], Ts ... ps);
But why specifying it is not possible?
As discussed in the linked to Q&A, although it "would make sense for a compiler to support this", the standard does not, and a leading template parameter pack greedily includes all explicitly provided template arguments, even those that would not be valid as part of the expanded pack (e.g. non-type template arguments to a type template parameter pack).

Template empty bracket initialization deduction

I think template functions can have default arguments parameters (not template parameters but runtime parameters). We can also initialize a class with an empty bracket initialization. But how does the compiler match the template ?
Why does this code compiles, how does the compiler make the deduction and what s Args in this function call example ?
What I have understand:
The default bracket initialization call the empty constructor, implicitly created because there is no user-defined constructor or user-defined default constructor. That is, we can initialize any pack with {}.. So the deduction don't apply there because we can't choose one pack, every pack is candidate. Maybe the default variadic template argument is <> (no arguments).
template<typename...> class pack {};
template<class... Args>
inline auto make(pack<Args...> = {}) {
}
int main() { make(); }
(compiled with GCC)
Note: I thought it wasn't, but default argument can be useful: 2 methods of calling the function: make < int, char, int >() (normal use) or make(myPack) for packing a variadic.
Given make();, the deduced Args is empty; make(); has the same effect as make<>(); in this case.
The template parameter is a parameter pack, and no template arguments are provided here. Note that function default arguments don't participate in template argument deduction. Then Args is deduced as empty.
If a parameter pack appears as the last P, then the type P is matched against the type A of each remaining argument of the call. Each match deduces the template arguments for the next position in the pack expansion:
Type template parameter cannot be deduced from the type of a function default argument:

How can a template parameter pack have both explicit and deduced arguments?

g++, clang++, and MSVC (pre 2018) all accept the following C++17 code, resulting in the output "unsigned int" then "int":
#include <iostream>
void print_type(int) { std::cout << "int\n"; }
void print_type(unsigned int) { std::cout << "unsigned int\n"; }
template <typename ...T>
void print_types(T ...args)
{
(print_type(args),...);
}
int main()
{
print_types<unsigned int>(1, 1);
}
I agree that this ought to work this way, but I'm having trouble finding a description of why and exactly how in the Standard.
First there's [temp.deduct]/2 describing the processing of explicit template arguments before doing the rest of template argument deduction:
[T]he following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:
... There must not be more arguments than there are parameters unless at least one parameter is a template parameter pack, and there shall be an argument for each non-pack parameter....
The specified template argument values are substituted for the corresponding template parameters as specified below.
In the example, unsigned int is certainly a "specified template argument value". But if its "corresponding template parameter" T gets substituted now, it's difficult to see how it could become a longer list of types later.
For the template argument deduction process, there's [temp.deduct.call]/1:
For a function parameter pack that occurs at the end of the parameter-declaration-list, deduction is performed for each remaining argument of the call, taking the type P of the declarator-id of the function parameter pack as the corresponding function template parameter type. Each deduction deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack.
I take "remaining argument of the call" here to mean arguments after the ones that correspond to function parameters that are not the final function parameter pack. But that would mean that in my example, the first function argument 1 is used to deduce T=int. Does this deduction actually happen, but then get discarded/overridden by the T=unsigned int from the explicit template argument?
Or maybe "remaining argument of the call" is supposed to mean function arguments after the ones that do not correspond to the final function parameter pack AND after any that correspond to parameter types generated from explicit template arguments; and "subsequent positions in the template parameter packs expanded by the function parameter pack" is supposed to mean sequential positions after any filled by explicit template arguments, but this would be far from clear. And if so, it's also confusing that there is a list of parameter types associated with the function parameter pack but it's still a function parameter pack.
[Another possible implementation giving the expected behavior would be: when one or more explicit template arguments A_1, ..., A_k correspond to a template parameter pack P, invent another template parameter pack More_P of the same kind, and substitute each expansion of P with the template argument list {A_1, ..., A_k, More_P...}. Then More_P can be deduced like any other template parameter pack. If More_P is never deduced, substitute an empty list for all its expansions before evaluating semantics as for all other deduced substitutions. But there's even less justification for this interpretation in the Standard.]
Have I missed something in the Standard that better describes how explicit template arguments and deduced template arguments can work together to form a single list for one template parameter pack?
It's [temp.arg.explicit]/8:
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments. [ Example:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types is deduced to the sequence int*, float*, int
}
— end example ]

Empty variardic packs of enums -- do they make two functions different?

There is a technique I sometimes use when overriding template functions that goes like this:
#include <utility>
template<int> struct unique_enum { enum class type {}; };
template<int index> using UniqueEnum = typename unique_enum<index>::type;
template<bool b, int index=1>
using EnableFuncIf = typename std::enable_if< b, UniqueEnum<index> >::type;
template<bool b, int index=1>
using DisableFuncIf = EnableFuncIf<!b, -index>;
// boring traits class:
template<typename T>
struct is_int : std::false_type {};
template<>
struct is_int<int> : std::true_type {};
#include <iostream>
// use empty variardic packs to give these two SFINAE functions different signatures:
template<typename C, EnableFuncIf< is_int<C>::value >...>
void do_stuff() {
std::cout << "int!\n";
}
template<typename C, DisableFuncIf< is_int<C>::value >...>
void do_stuff() {
std::cout << "not int!\n";
}
int main() {
do_stuff<int>();
do_stuff<double>();
}
This distinguishes do_stuff from do_stuff, because one takes 0 or more UniqueEnum<1>s, and the other takes 0 or more UniqueEnum<-1>s. gcc 4.8 considers these different empty packs to be distinct.
However, in the latest version of clang I tried, this fails: it treats the function with 0 UniqueEnum<1>s as being the same as the function with 0 UniqueEnum<-1>s.
There are easy workarounds that work in clang, but I'm wondering if my above technique is legal -- do two function templates, which differ only by empty variardic parameter packs, actually different?
I think GCC is right, and your technique is correct. Basically, since the type argument for C is specified explicitly, the question is whether:
a. substitution of C everywhere else in the function template signature happens first, and then type deduction is performed (which should result in a substitution failure); or
b. type deduction is performed first, and then substitution is performed (which would not result in a substitution failure, because the corresponding argument pack would be empty, and so there would be no substitution to perform).
It seems GCC assumes (1), while Clang assumes (2). Paragraph 14.8.2/2 of the C++11 Standard specifies:
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:
— 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 at least one parameter
is a template parameter pack, and there shall be an argument for each non-pack parameter. Otherwise,
type deduction fails.
— Non-type arguments must match the types of the corresponding non-type template parameters, or must
be convertible to the types of the corresponding non-type parameters as specified in 14.3.2, otherwise
type deduction fails.
— The specified template argument values are substituted for the corresponding template parameters as
specified below.
The following paragraph then says:
After this substitution is performed, the function parameter type adjustments described in 8.3.5 are performed. [...]
Moreover, paragraph 14.8.2/5 specifies:
The resulting substituted and adjusted function type is used as the type of the function template for template
argument deduction. [...]
Finally, paragraph 14.8.2/6 goes as follows:
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.
This all seems to imply that first substitution is performed, then template argument deduction. Hence, a substitution failure should occur in either case and one of the two templates should be discarded from the overload set.
Unfortunately, there does not seem to be a clear specification as to what the behavior should be when templates arguments are deduced rather than being explicitly specified.