template function call with empty angle brackets <> - c++

I am confused with the below template behavior, where it compiles fine with the empty angle brackets (template without parameters) since syntactically, template<> is reserved to mark an explicit template specialization.
template <typename T> void add(T a, T b) { }
int main() {
add<>(10, 3); // compiles fine since both parameters are of same data type
add<>(10, 3.2); // Error: no matching function for call to add(int, double)
}
In the above case is the template parameter really optional?

template<> is reserved to mark an explicit template specialization.
It means various things, depending on context. Here it means "use the default or deduced argument", just as if you simply said add.
In the first case, both function arguments have the same type, so the template argument can be deduced as int.
In the second case, they have different types, so the template argument can't be deduced. You'd have to specify what you want, e.g. add<double>, convert one function argument to match the other, or modify the template to parametrise each type separately.
In the above case is the template parameter really optional?
Yes, if it can be deduced from the argument types.

In the first case, yes because the can be inferred through the standard's rules. In the second, no because they can't - you'd have to write something like:
add<float>(10, 3.2);

You have a single template parameter and two function parameters of different types. Template argument deduction needs to match for both arguments, but if you supply an int and a double, it doesn't work. The reason is that deduced argument have to an exact match and type conversions are not considered.
The syntax
add<double>(10, 3.2);
would explicitly force T to be equal to double. In that case, the int constant 10 is converted to double.
You could also add another overload
template <typename T, typename U> void add(T a, U b) { }
and possibly constrain that using SFINAE by requiring that is_convertible<T, U>

Related

Why does the compiler try to deduce when an argument-less function template is provided?

template <typename... T>
struct X {
static_assert(sizeof...(T) != 0);
};
template <typename... T>
void f(const X<T...> &) {}
template <typename T>
void inner() {}
int main() {
f(inner);
}
The static assert fires in this example.
Why does the compiler try to deducing anything here? And then it apparently even tries to instantiate X (with empty type argument list?..)...
Why the error isn't just 'template used without arguments'?..
If I change inner function to be a struct, the compiler reports:
use of class template 'inner' requires template arguments
which makes sense; if the param is just const X &, there's
declaration type contains unexpanded parameter pack 'T'
which also makes sense, however is less clear than the case with struct, because it reports an issue at the callee, not at the call site.
If the param is const X<T> &, the report is also a bit weird at first sight:
candidate template ignored: couldn't infer template argument 'T'.
These errors are for Clang 14, but GCC also reports similar ones.
Is the instantiation here somehow specified by the standard? If so, how? Also, why does it result in an empty type list?
This is mostly due to [temp.arg.explicit]/4, applicable when source names a function template, and emphasis mine:
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 not otherwise deduced will be deduced as an empty sequence of 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.
The function template name as an argument means it is not used for template argument deduction of f ([temp.deduct.type]/(5.5.3)). So the pack T is not deduced from the function argument list (inner), and [temp.arg.explicit]/4 applies and deduces T as an empty list of types.
Now to evaluate the expression f(inner) involves converting the expression inner to the parameter type const X<>&. Whether and how this is valid depends on the constructors of class X<>, so the template is instantiated, causing the static_assert error since the template parameter pack does have zero elements.
Because the argument refers to an overload set that contains a function template X, no deduction is attempted from it. The hope is that the deduction will succeed anyway (perhaps from other arguments) and then the template arguments of X can be deduced from the resulting argument type. (Of course, it’s impossible to deduce the template argument for inner, but even that of
template<class T>
T make();
can be deduced in certain contexts, so it’s not in general a vain hope.)
Here, deduction for f does succeed vacuously, with T as an empty pack. (This is much like a default template argument.) Then const X<>& is the parameter type, and so overload resolution is attempted to construct an X<> from inner. That obviously depends on the constructors for X<>, so that type is completed and the compilation fails.

Which part of the C++ standard prevents explicitly specifying this template's arguments?

In my C++ travels I've come across the following idiom (for example here in Abseil) for ensuring that a templated function can't have template arguments explicitly specified, so they aren't part of the guaranteed API and are free to change without breaking anybody:
template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);
And it does seem to work:
foo.cc:5:3: error: no matching function for call to 'AcceptSomeReference'
AcceptSomeReference<char>('a');
^~~~~~~~~~~~~~~~~~~~~~~~~
foo.cc:2:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'ExplicitArgumentBarrier'
I understand at an intuitive level why this works, but I'm wondering how to make it precise. What section(s) of the standard guarantee that there is no way to explicitly specify template arguments for this template?
I'm surprised by clang's excellent error message here; it's like it recognizes the idiom.
The main reason that a construct of the form
template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(T const&);
prohibits you from specifying the template argument for T explicitly is that for all template parameters that follow a template parameter pack either the compiler must be able to deduce the corresponding arguments from the function arguments (or they must have a default argument) [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]). A template parameter of a deduction guide template ([temp.deduct.guide]) that does not have a default argument shall be deducible from the parameter-type-list of the deduction guide template.
As pointed out by #DavisHerring this paragraph alone does not necessarily mean that it must be deducted. But: Finding the matching template arguments is performed in several steps: First the explicitly specified template argument list will be considered [temp.deduct.general]/2, only later on the template type deduction will be performed and finally default arguments are considered [temp.deduct.general]/5. [temp.arg.explicit]/7 states in this context
Note 3: Template parameters do not participate in template argument deduction if they are explicitly specified
This means whether a template argument can be deducted or not depends not only on the function template declaration but also on the steps before the template argument deduction is applied such as the consideration of the explicitly specified template argument list. A template arguments following a template parameter pack can't be specified explicitly as then it would not be participating in template argument deduction ([temp.arg.explicit]/7) and therefore would also not be deducted (which would violate [temp.param]/14).
When explicitly specifying the template arguments the compiler will therefore match them "greedily" to the first parameter pack (as for the following arguments either default values should be available or they should be deductible from function arguments! Counter-intuitively this is even the case if the template arguments are type and non-type parameters!). So there is no way of fully explicitly specifying the template argument list of a function with a signature
template <typename... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);
If it is called with
AcceptSomeReference<int,void,int>(8.0);
all the types would be attributed to the template parameter pack and never to T. So in the example above ExplicitArgumentBarrier = {int,void,int} and T will be deducted from the argument 8.0 to double.
To make things even worse you could use a reference as template parameter
template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T&);
It isn't impossible to explicitly specify template arguments for such a barrier but reference template parameters are very restrictive and it is very unlikely to happen by accident as they have to respect [temp.arg.nontype]/2 (constexpr for non-types) and [temp.arg.nontype]/3 (even more restrictive for references!): You would need some sort of static variable with the correct cv-qualifier (e.g. something like static constexpr int x or static int const x in the following example would not work either!) like in the following code snippet (Try it here!):
template <int&... ExplicitArgumentBarrier, typename T>
void AcceptSomeReference(const T& t) {
std::cout << t << std::endl;
return;
}
static int x = 93;
int main() {
AcceptSomeReference<x>(29.1);
return EXIT_SUCCESS;
}
This way by adding this variadic template of unlikely template arguments as the first template argument you can hold somebody off from specifying any template parameters at all. Whatever the users will try to put will very likely fail compiling. Without an explicit template argument list ExplicitArgumentBarrier will be auto-deducted to have a length of zero [temp.arg.explicit]/4/Note1.
Additional comment to #DavisHerring
Allowing somebody to explicitly specify the template arguments following a variadic template would lead to ambiguities and break existing rules. Consider the following function:
template <typename... Ts, typename U>
void func(U u);
What would the template arguments in the call func<double>(8.0) be? Ts = {}, U = double or Ts = {double}, U = double? The only way around this ambiguity would be to allow the user to only specify all the template arguments explicitly (and not only some). But then this leads again to problems with default arguments:
template <typename... Ts, typename U, typename V = int>
void func2(U u);
A call to func2<double,double>(8.0) is again ambiguous (Ts = {}, U = double, V = double or Ts = {double}, U = double, V = int). Now you would have to ban default template arguments in this context to remove this ambiguity!
The standard is very vague about several aspects of template argument usage, including which template parameter “corresponds” to each argument. [temp.arg]/1 merely says
When the parameter declared by the template is a template parameter pack, it will correspond to zero or more template-arguments.
which can be read as saying that any and all template arguments (past those for any prior template parameters) not just can but do correspond to the template parameter pack.
Note that it is still possible to specify template arguments for the pack explicitly—the point is that it’s useless, since you can’t for the following (i.e., meaningful) template parameters. The use of a type like int& is just to make it less likely that you would succeed in doing so accidentally and think it accomplished something.

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).

Substitution failure with `std::function` and previously deduced template parameter - why?

Consider the following code:
template <typename>
struct S { };
void g(S<int> t);
template <typename T>
void f(T, std::function<void(S<T>)>);
When attempting to invoke
f(0, g);
I get the following error:
error: no matching function for call to 'f'
f(0, g);
^
note: candidate template ignored: could not match
'function<void (S<type-parameter-0-0>)>'
against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
^
live example on godbolt.org
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context
In this case T can first be deduced by the passed argument 0, and then substituted into std::function<void(S<T>)> to get std::function<void(S<int>)>.
I would expect that after deducing T=int, the compiler would substitute T everywhere in the signature and then attempt to construct the std::function parameter with the argument g.
Why is that not the case? I presume that the ordering in which substitution/deduction happens has something to do with this, but I'd like to see the relevant Standard wording.
Bonus question: is this something that could potentially be changed in a future Standard while preserving backwards compatibility, or is there a fundamental reason why this kind of substitution doesn't work?
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context.
It is not a non-deduced context. Quite the contrary. Because deduction for the parameter of std::function is attempted, but the argument is not a std::function, deduction fails. The deduction of template arguments from function arguments must agree for all function arguments. If it fails for one, it fails entirely.
[temp.deduct.type]
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.
Making the type of the second function parameter into a non-deduced context is actually how one can overcome the error.
#include <functional>
template<typename T>
struct type_identity {
using type = T;
};
template <typename>
struct S { };
void g(S<int> ) {}
template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}
int main() {
f(0, g);
}
T is deduced successfully from the first function argument, and there is nothing left to deduce. So the dedcution is deemed a success.
Live
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context, in this case T can first be deduced by the passed argument 0.
This is not true. T is deduceable in this context. If you change the code to
template <typename T>
void f(std::function<void(S<T>)>);
int main()
{
f(std::function<void(S<int>)>(g));
}
the code would compile and T is correctly deduced.
Your issue is that you are passing an object to the function that it can't extract T from. The compiler will not do any conversion of the function arguments when it tries to deduce T. That means you have a int and a function as the types passed to the function. It gets int from 0, then tries to get the type from the std::function you pass in the second parameter but since you didn't pass a std::function it can't extract T and because of that, you get an error.

Why can't my C++ compiler deduce template argument for boost function?

I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}