Overloaded function template disambiguation with `std::enable_if` and non-deduced context - c++

Consider the following code:
template <typename T>
struct dependent_type
{
using type = T;
};
template <typename T>
auto foo(T) -> std::enable_if_t<std::is_same<T, int>{}>
{
std::cout << "a\n";
}
template<typename T>
void foo(typename dependent_type<T>::type)
{
std::cout << "b\n";
}
The first overload of foo can deduce T from its invocation.
The second overload of foo is a non-deduced context.
int main()
{
foo<int>( 1 ); // prints "b"
foo<double>( 1.0 ); // prints "b"
foo( 1 ); // prints "a"
}
Why does foo<int>( 1 ) print "b" and not "a"?
wandbox example

Essentially the partial ordering rules say that the dependent_type overload is more specialized because of that non-deduced context.
The process for ordering template functions is based on transforming the template function types and performing template deduction on each in turn, once going from the first template (the one taking T) to the second (the one taking dependent_type), then from the second to the first.
The rules are far too complex to replicate here, but go read [temp.func.order] and the passages it links to if you want the gory details. Here's a quick simplification:
For each template parameter of the template function, make up a unique type and replace the parameter with that. The transformed types for this example are:
void foo(UniqueType); //ignoring the SFINAE for simplicity
void foo(typename dependent_type<UniqueType>::type);
We then perform template deduction in two directions: once using the parameters of the first template as arguments to the second, and once using the parameters of the second as arguments to the first. This is akin to performing deduction on these function calls:
//performed against template <class T> void foo(typename dependent_type<T>::type);
foo(UniqueType{});
//performed against template <class T> void foo(T);
foo(dependent_type<UniqueType>::type{});
In carrying out these deductions, we're trying to discern whether one overload is more specialized then the other. When we try the first one, deduction fails, since typename dependent_type<T>::type is a non-deduced context. For the second one, deduction succeeds because dependent_type<UniqueType>::type is just UniqueType, so T is deduced to UniqueType.
Since deduction failed going from the second template to the first, the second template is taken as being more specialized than the first. The final result is that the overload resolution prefers the second template for foo<int>(1).

Related

more than one partial specialization matches the template argument list

When I run the program below, I get an error saying that more than one specialization matches the template argument list of class X<int, int*, 10>. The compiler obviously can't decide whether to use "Specialization 1" or "Specialization 2". I don't understand why that is the case because I thought that the compiler determines the level of specialization based on the number of template parameters. So, since "Specialization 1" has 2 template parameters (template<class T, int I>), and "Specialization 2" has only 1 template parameter (template<class T>) I figured that "Specialization 2" is more specialized than "Specialization 1". Obviously my reasoning is incorrect, so could someone explain the logic behind deducing which template is more specialized?
#include <iostream>
using namespace std;
template<class T, class U, int I> struct X {
void f() { cout << "General Template" << endl; }
};
template<class T, int I> struct X<T, T*, I> {
void f() { cout << "Specialization 1" << endl; }
};
template<class T> struct X<int, T*, 10> {
void f() { cout << "Specialization 2" << endl; }
};
int main() {
X<int, int*, 10> f;
f.f();
return 0;
}
This topic is unfortunately very complicated! I'm basing myself on this answer and cppreference, while quoting from the current working draft of the standard.
Firstly, according to "Matching of partial specializations":
A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list, and the deduced template arguments satisfy the associated constraints of the partial specialization, if any.
In your example, the context is the statement X<int, int*, 10> f;, which matches both partial specializations:
template<class T, int I> struct X<T, T*, I> {...} // -> T = int, I = 10
template<class T> struct X<int, T*, 10> {...} // -> T = int
Since this is the case, further checking is required to determine whether one of these candidates is more specialized than the other. That is described under "Partial ordering of partial specializations":
For two 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 and associated constraints 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.
That is very wordy, but as shown by the example it simply comes down to writing the partial specializations as function arguments:
template<class T, int I> void f(X<T, T*, I>); // #1
template<class T> void f(X<int, T*, 10>); // #2
From there we have to look at a different section about the "Partial ordering of function templates". Quoting the relevant paragraphs:
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. [...]
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].
In other words, transform #1 and #2 (again) by "instantiating" each with unique arguments, that is, different from any other arguments present:
void f(X<Ut1, Ut1*, Uv1>); // #A
void f(X<int, Ut2*, 10>); // #B
And then use their "function type", being the return type and the argument types, to perform deduction according to yet another section, "Deducing template arguments during partial ordering":
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.
Ok, going by the example on cppreference:
// #1 from #B:
// void(X<T, T*, I>) from void(X<int, Ut2*, 10>): deduction fails
// #2 from #A:
// void(X<int, T*, 10>) from void(X<Ut1, Ut1*, Uv1>): deduction fails
At least, I think that's the case, and therefore the result of this whole ordeal would be that neither template is more specialized than the other.

Why are variadic templates different than non-variadic, for only one argument?

This code compiles just fine:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename T>
void NonVariadicFunc(Struct<T>);
int main() {
NonVariadicFunc<int>(ConvertsToStruct{});
return 0;
}
But an attempt to make it a little more generic, by using variadic templates, fails to compile:
template <typename T1>
struct Struct {
};
struct ConvertsToStruct {
operator Struct<int>() const;
};
template <typename... T>
void VariadicFunc(Struct<T...>);
int main() {
VariadicFunc<int>(ConvertsToStruct{});
return 0;
}
What's going wrong? Why isn't my attempt to explicitly specify VariadicFunc's template type succeeding?
Godbolt link => https://godbolt.org/g/kq9d7L
There are 2 reasons to explain why this code can't compile.
The first is, the template parameter of a template function can be partially specified:
template<class U, class V> void foo(V v) {}
int main() {
foo<double>(12);
}
This code works, because you specify the first template parameter U and let the compiler determine the second parameter. For the same reason, your VariadicFunc<int>(ConvertsToStruct{}); also requires template argument deduction. Here is a similar example, it compiles:
template<class... U> void bar(U... u) {}
int main() {
bar<int>(12.0, 13.4f);
}
Now we know compiler needs to do deduction for your code, then comes the second part: compiler processes different stages in a fixed order:
cppreference
Template argument deduction takes place after the function template name lookup (which may involve argument-dependent lookup) and before template argument substitution (which may involve SFINAE) and overload resolution.
Implicit conversion takes place at overload resolution, after template argument deduction. Thus in your case, the existence of a user-defined conversion operator has no effect when compiler is doing template argument deduction. Obviously ConvertsToStruct itself cannot match anything, thus deduction failed and the code can't compile.
The problem is that with
VariadicFunc<int>(ConvertsToStruct{});
you fix only the first template parameter in the list T....
And the compiler doesn't know how to deduce the remaining.
Even weirder, I can take the address of the function, and then it works
It's because with (&VariadicFunc<int>) you ask for the pointer of the function (without asking the compiler to deduce the types from the argument) so the <int> part fix all template parameters.
When you pass the ConvertToStruct{} part
(&VariadicFunc<int>)(ConvertToStruct{});
the compiler know that T... is int and look if can obtain a Struct<int> from a ConvertToStruct and find the apposite conversion operator.

Overload resolution with template parameters

I am having trouble understanding why the following leads to an ambiguous call:
#include <iostream>
// generic version f(X, Y)
template <class X, class Y>
void f(X x, Y y) {
std::cout << "generic" << std::endl;
}
// overload version
template <class X>
void f(X x, typename X::type y) {
std::cout << "overload" << std::endl;
}
struct MyClass {
using type = int;
};
int main() {
f(MyClass(), int()); // Call to f is ambiguous
}
I would expect the overload version, which is more specialised in the second argument than the generic version, to be selected as the best candidate. I know that if I change the overload version to
template <class X>
void f(X x, int y) {
std::cout << "overload" << std::endl;
}
then the call is resolved just fine, which means it has to do with the fact that X::type is a template dependent name, but still cannot work out why it fails. Any help is much appreciated.
First, we pick the viable candidates. Those are:
void f(MyClass, int); // with X=MyClass, Y=int
void f(MyClass, typename MyClass::type); // with X=MyClass
Those candidates both take the same arguments, so have equivalent conversion sequences. So none of the tiebreakers based on those apply, so we fall back to the last possible tiebreaker in [over.match.best]:
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 [...] 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 14.5.6.2.
So we try to order the two function templates based on the partial ordering rules, which involve synthesizing a unique type for each template parameter and attempting to perform template deduction against each over overload. But with a key additional relevant rule from [temp.deduct.partial]:
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.
So what does this mean. First, let's try to deduce the generic version from the overload. We pick synthetic types UniqueX (for X) and UniqueX_type (for typename X::type) and see if we can invoke the generic function. This succeeds (with X=UniqueX and Y=typename X::type).
Let's try the inverse. We pick a UniqueX (for X) and a UniqueY (for Y) and try to perform template deduction. For the first P/A pair, this trivially succeeds. But for the second argument, X is a non-deduced context, which you would think would mean that template deduction fails. BUT as per the bolded part of the quote, we just skip this P/A pair for the purposes of ordering. So, since the first P/A pair succeeded, we consider the entire deduction process to have succeeded.
Since template deduction succeeds in both directions, we cannot pick one function or the other as being more specialized. Since there are no further tiebreakers, there is no single best viable candidate, so the call is ambiguous.
When the second overload is changed to:
template <class X> void f(X, int);
the part of the process that changes is that deduction now fails in one direction. We can deduce X=UniqueX but the second pair has a parameter of type int and an argument of type UniqueY, which will not work, so this direction fails. In the reverse direction, we can deduce X=UniqueX and Y=int. That makes this overload more specialized that the generic overload, so it would be preferred by the last tiebreaker I originally mentioned.
As an addendum, note that partial ordering of templates is complicated. Consider:
template <class T> struct identity { using type = T; };
template <class T> void foo(T ); // #1
template <class T> void foo(typename identity<T>::type ); // #2
template <class T> void bar(T, T); // #3
template <class T> void bar(T, typename identity<T>::type ); // #4
foo(0); // calls #1, #2 isn't even viable
foo<int>(0); // calls #2
bar(0,0); // calls #3! we fail to deduce 3 from 4, but we succeed
// in deducing 4 from 3 because we ignore the second P/A pair!

Overload resolution and explicit template arguments

The following code prints "func 2".
Why does the compiler treat the second template as a better match, in presence of explicit (not deduced) template arguments? Why is there no ambiguity?
I'd appreciate quotations from the C++ standard.
#include <iostream>
template<class T>
struct identity
{
typedef T type;
};
template<class T>
void func(T)
{
std::cout << "func 1\n";
}
template<class T>
void func(typename identity<T>::type)
{
std::cout << "func 2\n";
}
int main()
{
func<int>(1);
}
Both candidates are viable and take identical arguments, so the overload resolution process falls back to the last tiebreaker: partial ordering of function templates [temp.func.order].
The rule is that we synthesize a new type for each template type parameter and attempt to perform deduction on each other overload. For 1, we synthesize a type Unique1, which fails deduction on 2 because T is a non-deduced context. For 2, we synthesize a type Unique2, which succeeds deducing T = typename identity<Unique2>::type. Since deduction succeeds in one direction and not the other, that makes 2 more specialized than 1, hence it's preferred.
Note that template partial ordering rules are somewhat incomplete in the standard. If you simply add another argument of type T, the preference flips.

function overload matching template template

I would expect the last two lines of the first code example to print the same.
The types are deducted as I expect and the the overload resolution is also as I expect.
However, if I explicitly type qualify the function call, then I get a different result then when the type is deduced.
The second code example repeats the exercise replacing overload resolution with specialization. In that case everything works as anyone would expect.
Any explanation?
EDIT: I added one more line showing what Karthik was mentioning regarding print<R,int>(r); which I also do not understand.
Code Example 1: (function template overloading)
#include <iostream>
template <typename T>
void print (T i)
{
std::cout << "simple" << std::endl;
}
template <template<typename> class FF, typename TT>
void print (FF<TT> i)
{
std::cout << "template" << std::endl;
}
template <typename T1, typename T2>
void print (T1 a)
{
T2 b;
std::cout << "two type parameters" << std::endl;
}
template <>
void print<int>(int i)
{
std::cout << "int" << std::endl;
}
template <typename T>
struct R
{
T x;
};
int main()
{
R<int> r;
print<int>(1.1); // ok, prints "int"
print(1.1); // ok, prints "simple"
print<int>(1); // ok, prints "int"
print(1); // ok, prints "int"
print(r); // ok, prints "template"
print<int,int>(1); // ok, prints "two type parameters"
print<R<int>,int>(r); // ok, prints "two type parameters"
print<R<int> >(r); // (1) ?? why "simple" ??
print<R,int >(r); // (2) ?? prints "template", why does it compile at all ??
// gcc 4.6.2 (-std=c++0x) and 4.8.1 (-std=c++11)
// clang++ 3.3.1 same behavior as gcc
}
Code Example 2: (class template specialization).
#include <iostream>
template <typename T>
struct P
{
static void print (T i)
{
std::cout << "simple" << std::endl;
}
};
template <template<class TT> class FF, typename TT>
struct P <FF<TT> >
{
static void print (FF<TT> i)
{
std::cout << "template" << std::endl;
}
};
template <>
struct P<int>
{
static void print(int i)
{
std::cout << "int" << std::endl;
}
};
template <typename T>
struct R
{
T x;
};
int main()
{
R<int> r;
P<double>::print(1.1); // ok, prints "simple"
P<int>::print(1); // ok, prints "int"
P<R<int> >::print(r); // ok, prints "template"
//P<R,int >::print(r); // ok, does not compile
}
Well, let's look at what the compiler thinks of each of these.
template <typename T> void print (T i); // (1)
template <template<typename> class FF, typename TT> void print (FF<TT> i); // (2)
template <typename T1, typename T2> void print (T1 a); // (3)
template <> void print<int>(int i); // (4)
Ok, some preliminaries: we have three function templates here that overload each other (1, 2, and 3), and 4 is a specialization of 1.
All three overloads have a single function parameter. In addition, the functions have template parameters:
1 has a single type template parameter that can be deduced from the function parameter.
2 has a template template parameter and a type template parameter, both of which can be deduced from the function parameter.
3 has two type template parameters, only the first of which can be deduced (making the deduction useless).
Now let's look at the calls. When there are explicit template arguments, the compiler will always "pre-filter" the overloads for those functions that can be instantiated that way.
print<int>(1.1); // ok, prints "int"
One explicit type template argument. 1 matches. 2 doesn't match, because the first argument isn't a template. 3 matches, fixing T1 to int; however, T2 cannot be deduced, so it falls away, too. 1 is chosen with the parameter T being int. This matches the specialization 4.
print(1.1); // ok, prints "simple"
No explicit template arguments. Deduction starts; the argument type is double. 1 matches; T is double. 2 requires the pattern FF<TT>, and double doesn't match that, so failure. 3 can deduce T1 to double, but has nothing for T2, so it fails too. 1 is chosen. The specialization doesn't match.
print<int>(1); // ok, prints "int"
This is identical to the first case, except that during final overload resolution, an implicit conversion happens.
print(1); // ok, prints "int"
This is identical to the second case, except that the deduced type is int (still doesn't match FF<TT>), so the specialization matches.
print(r); // ok, prints "template"
Deduction gives the following results: 1 matches, with T = R<int>. For 2, R<int> matches the pattern FF<TT>, so it is viable, with FF = R and TT = int. 3, as usual, doesn't know what to do with T2. Overload resolution gets identical sequences for 1 and 2 (identity), so partial function template ordering resolves the ambiguity: 2 is more specialized than 1 and is chosen.
print<int,int>(1); // ok, prints "two type parameters"
Two explicit type template arguments. 1 only accepts one. 2 wants a template as the first argument. 3 is left.
print<R<int>,int>(r); // ok, prints "two type parameters"
This is identical to the previous case. The first argument is R<int> instead of int, but that's still just a type and 2 doesn't like it.
print<R<int> >(r); // (1) ?? why "simple" ??
This is identical to the first and third cases. We have one explicit type template argument. 3 fails to deduce T2, 2 wants a template as the first argument, so 1 is the only choice. R<int> is a type, not a template.
print<R,int >(r); // (2) ?? prints "template",
Here, we have two explicit template arguments, the first a template, the second a type. 1 only accepts one argument. 3 wants a type for its first template parameter. 2 is happy to take the template for its first and the type for its second parameter.
The key lessons here are:
Explicit template arguments are matched to the template parameters before any deduction or overload resolution happens, so only some functions match in the first place.
1 and 2 are overloads, and overload resolution happens separately for them. It would be different if 2 was a specialization of 1, but partial function template specialization doesn't exist.
It's only a template until you give it arguments. An instantiated function template is a function. An instantiated class template is a class (and thus a type). There is no difference between the instantiation of a class template and a non-template class, except for the pattern matching that argument deduction and partial specialization do.
Edit: To answer the expanded question.
When I replace function overloading with template specialization, then template pattern matching works as I would have expected. I have some trouble believing that the pattern matching rules also differ between classes and functions.
This is a matter of perspective. There are no pattern matching rules for classes and functions, so you can't say that they differ or not. There are pattern matching rules for partial specialization and for template argument deduction. These are actually the same; the section on partial specialization (14.5.5) refers to the section on function template argument deduction (14.8.2).
So the pattern matching rules are the same.
However, argument deduction only applies to functions (there's no argument deduction for class templates, at least not yet), while partial specialization only applies to classes (you can't partially specialize functions). This is the key difference between functions and classses: in your first example, you have two function templates:
template <typename T> void print(T i);
template <template <typename> class FF, typename TT> void print(FF<TT> i);
These are two different templates. They are completely independent. It's up to the complicated rules and interactions of explicit parameter passing, argument deduction, and overload resolution to determine which one is meant in any given invocation. However, and this is important, each can exist without the other. In other words, imagine you had only one function:
template <template <typename> class FF, typename TT> void something_else(FF<TT> i);
Would you then be surprised that something_else<R, int>(r); is valid? You have a template with two parameters, and you pass two arguments. The existence of another template with just one argument doesn't change that!
This is important, so I'll repeat it: Two function templates, even if they have the same name, are completely independent templates.
Not so with classes. If you try the same thing with classes, the compiler will complain:
template <typename T> class Q {};
template <template <typename> class FF, typename TT> class Q {};
Clang says:
redef.cc:2:5: error: too many template parameters in template redeclaration
You cannot have two class templates with the same name. The compiler thinks you want to declare the old Q again, and complains that the template parameter lists don't match.
The only thing you can do with class templates is specialize them, as you did in your second example:
template <typename T> class P {};
template <template <typename> class FF, typename TT> class P<FF<TT>> {};
But note that these are not independent templates. They are not even the same thing. The first is a class template, whereas the second is a class template partial specialization. The second is completely dependent on the first; removing the primary template means the specialization no longer compiles:
redef.cc:2:64: error: explicit specialization of non-template class 'P'
Unlike the overloaded function templates, a class template partial specialization is not an entity the user can reference. For the user, there is one template, P, and it has one template parameter. The specialization will match if this one parameter takes a specific form, but unlike the second function template in the first example, the specialization is not an independent template with two parameters.
This is why P<R<int>>::print(r) compiles and works: P has one parameter, and R<int> is passed for it. The partial specialization matches the pattern and is therefore chosen. But P<R, int>::print(r) does not work: P only has one template parameter, and here you're trying to pass two. The specialization is not its own entity and therefore is not considered.
But the function templates are all independent, full templates. Only the full specialization template <> void print<int>(int i) is not.
So to sum up:
Function templates can be fully specialized or overloaded. Overloaded function templates are fully independent: when you want to know what arguments you can or should explicitly supply, look at all their parameter lists in turn.
Avoid specializing function templates. It interacts with overloaded function templates in weird ways, and you can end up with a function different from what you expected being called. Just overload function templates, with other function templates and plain functions. The partial ordering rules are intuitive; just remember that when in doubt, a plain function is chosen over a template.
Class templates can be fully or partially specialized, but not overloaded. Specializations are not independent templates; when instantiating a class template, you always have to go to the primary template for the parameter list.
The matching rules for choosing a partial specialization and for deducing template arguments from function arguments are the same; however, when you explicitly pass template arguments to a function template, deduction is not performed for these parameters.
This is a guess not an answer, those with the standard at their finger tips can enlighten us all,
But let me take an educated guess
template <template<typename> class FF, typename TT> //......(1)
TT is a type while FF is considered a template template parameter
template <typename T> // .....(2)
T is a type
Now when Explicitly specifying the type, R<int>, it is a concrete type, thus (2) is picked.
When asking it to infer, I guess the compiler tries both options and (1) fits closer (or more specific), and thus that is chosen.
When explicitly specifying <R,int>, ofcourse we use the exact signature of (1) and that is why that one is picked.
+1 to the question, I would not have expected this either.
Perhaps some useful information can be found here
The line print< R<int> >(r); looks like a single-template-parameter print (it can't guess what you want) so it calls the single-template-parameter function with T = R<int>.
print< R,int >(r); calls for a two-template-parameter function, of which there is only one version (template). Luckily R is a template that can be instantiated with int so it compiles.
It would print simple because the type R<int> is deduced as int. In your case, you need to pass 2 parameters to explicitley make it deduce as template <template<typename> class FF, typename TT>