How are template specializations chosen with default arguments and more - c++

https://stackoverflow.com/a/22487113/4416169
in the following answer we can see the code in scrutiny:
#include <iostream>
#include <type_traits>
template <typename T, T N, typename = void >
struct X {
static const bool isZero = false;
};
template <typename T, T N>
struct X < T, N, typename std::enable_if<N == 0>::type > {
static const bool isZero = true;
};
int main(int argc, char* argv[]) {
std::cout << X <int, 0>::isZero << std::endl;
std::cout << X <int, 1>::isZero << std::endl;
return 0;
}
https://stackoverflow.com/a/35652391/4416169 here we can see how templates are chosen:
Choosing a template specialization happens in five steps:
Take the primary template declaration.
Fill in user-specified template arguments.
Function templates
only: Deduce additional template arguments.
Use defaults for
remaining template arguments.
Use the partial ordering
algorithm (C++14 14.5.6.2) to choose the best-matching
specialization.
firstly, im aware that SFINAE will take care of excluding the struct with the std::enable_if<N==0> as it will not have the ::type defined (making it compile to error, but of course substitution failure is NOT an ERROR, just exclude the template from the final specialization options, so beautiful) when N != 0. Since N!=0 always results in the formerly defined template being chosen(the unspecialized one), I wonder why is it that N=0 chooses the latterly defined template(the specialized one) over the former.
Thus following these steps we can further scrutinize(in the case of T=int and N=0):
template<typename T, T N, typename = void>
T is int, N is 0; template<int,0,(unnamed-type)>
do nothing we are not a function.
(unnamed-type) is void; template<int,0,void>
this is explained here: https://stackoverflow.com/a/17008568/4416169
We know now that when we try to access X<int,0>::isZero there are two options, the one with the default template type set to void by default, and the one where that same template type is set to void but not by default but explicitly.
Does the compiler always prefer the template(out of all available template specializations) with the least arguments?
Moreover and expanding: would this mean that the template with the least actual arguments is chosen over the one with the same amount of explicit(explicit as in non-defaulted or obligatory) arguments but that has the remainder set to a default value?
To be even more clear:
template<typename T, T N, typename C = void>
struct defTempArgStruct {
static constexpr unsigned int value = 0;
};
template<typename T, T N>
struct defTempArgStruct<T,N>
{
static constexpr unsigned int value = 99;
};
int main()
{
std::cout << defTempArgStruct<int, 2>::value << std::endl;
}
why does that code choose the second specialized template(<typename T, T N>),displaying 99, over the first? And why does it choose the first, displaying 0, when the second, specialized, one changes to be:
template<typename T, T N>
struct defTempArgStruct<T,N,float>
{
static constexpr unsigned int value = 99;
};
very confusing.
PLEASE be eager to correct anything anywhere that im getting wrong!

A viable partial specialization is always preferred over the primary template:
[temp.class.spec.match]/1 When a class template is used in a context that requires an instantiation of the class, it is necessary to determine whether the instantiation is to be generated using the primary template or one of the partial specializations. This is done by matching the template arguments of the class template specialization with the template argument lists of the partial specializations.
(1.1) — If exactly one matching specialization is found, the instantiation is generated from that specialization.
(1.2) — If more than one matching specialization is found, the partial order rules (17.5.5.2) are used to determine whether one of the specializations is more specialized than the others. If none of the specializations is more specialized than all of the other matching specializations, then the use of the class template is ambiguous and the program is ill-formed.
(1.3) — If no matches are found, the instantiation is generated from the primary template.

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 is the concept in template template argument not verified?

C++20 allows the program to specify concept for template template argument. For example,
#include <concepts>
template <typename T> concept Char = std::same_as<T, char>;
template <typename> struct S {};
template <template <Char U> typename T, typename U> T<U> foo() { return {}; }
int main() { foo<S, int>(); }
the first template argument of the function foo is expected to be a single argument template.
The concept Char is defined to be true only the type char, so an attempt to satisfy it for int shall fail. Still above program is accepted by all compilers: https://gcc.godbolt.org/z/PaeETh6GP
Could you please explain, why the concept in template template argument can be specified, but it will be still ignored?
A template (actual) argument matches a template (formal) parameter if the latter is at least as specialised as the former.
template <Char> typename T is more specialised than template <typename> struct S. Roughly speaking, template <Char> accepts a subset of what template <typename> accepts (the exact definition of what "at least as specialised" actually means is rather involved, but this is a zeroth approximation).
This means that the actual argument can be used in all contexts where the formal parameter can be used. That is, for any type K for which T<K> is valid, S<K> is also valid (because S<K> is valid for any K).
So it is OK to substitute S for T.
If you do it the other way around:
template<typename T> concept Any = true;
template<typename T> concept Char = Any<T> && std::same_as<T, char>;
template<template<Any> class T> void foo();
template<Char> struct S { };
int main()
{
foo<S>();
}
then this is ill-formed, because (roughly) you can say T<int> but not S<int>. So S is not a valid substitute for T.
Notes:
Why would we need this always-true concept Any? What's wrong with simply saying template <template <typename> typename>? Well, that's because of a special rule: if the parameter is not constrained at all, constraints of the argument are ignored, everything goes.
Why write Any<T> && std::same_as<T, char>;? To illustrate a point. The actual rules do not evaluate the boolean values of constraints, but compare constraints as formulae where atomic constraints serve as variables, see here. So the formal reason is that S has a conjunction of a strictly larger (inclusion-wise) set of atomic constraints than T. If S had the same or a strictly smaller set, it would be well-formed. If two sets are not ordered by inclusion, then neither template is more specialised, and there is no match.

Ambiguous partial specializations and enable_if_t

This question is due to insane curiosity rather than an actual problem.
Consider the following code:
template<typename...>
struct type_list {};
template<typename, typename = void>
struct test_class;
template<typename... T>
struct test_class<type_list<T...>> {
static constexpr auto value = false;
};
template<typename T>
struct test_class<type_list<T>, std::enable_if_t<std::is_same_v<T, int>>> {
static constexpr auto value = true;
};
int main() {
static_assert(!test_class<type_list<double, char>>::value);
static_assert(test_class<type_list<int>>::value);
}
This fails with the error:
ambiguous partial specializations of 'test_class<type_list>'
If I changed the second specialization to something that doesn't work from a functional point of view, the error would go away:
template<typename T>
struct test_class<type_list<T>> {
static constexpr auto value = true;
};
Similarly, if I use the alias template void_t, everything works as expected:
template<typename T>
struct test_class<type_list<T>, std::void_t<std::enable_if_t<std::is_same_v<T, int>>>> {
static constexpr auto value = true;
};
Apart from the ugliness of combining void_t and enable_if_t, this also gets the job done when there is a single type that differs from int, ie for a static_assert(!test_class<type_list<char>>::value) (it wouldn't work in the second case instead, for obvious reasons).
I see why the third case works-ish, since the alias template is literally replaced with void when the condition of the enable_if_t is satisfied and type_list<T> is more specialized than type_list<T...> (right?).
However, I would have expected the same also for the following:
template<typename T>
struct test_class<type_list<T>, std::enable_if_t<std::is_same_v<T, int>>> {
static constexpr auto value = true;
};
At the end of the day, std::enable_if_t<std::is_same_v<T, int>> somehow is void when the condition is statisfied (ok, technically speaking it's typename blabla::type, granted but isn't ::type still void?) and therefore I don't see why it results in an ambiguous call. I'm pretty sure I'm missing something obvious here though and I'm curious to understand it now.
I'd be glad if you could point out the standardese for this and let me know if there exists a nicer solution than combining void_t and enable_if_t eventually.
Let's start with an extended version of your code
template<typename, typename = void>
struct test_class;
template<typename T>
struct test_class<type_list<T>> {
static constexpr auto value = false;
};
template<typename... Ts>
struct test_class<type_list<Ts...>> {
static constexpr auto value = false;
};
template<typename T>
struct test_class<type_list<T>, std::enable_if_t<std::is_same_v<T, int>>> {
static constexpr auto value = true;
};
that is called with
test_class<type_list<int>>::value
Try it here!
The standard distinguishes between template parameters that are equivalent, ones that are only functionally equivalent and others which are not equivalent [temp.over.link]/5
Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one-definition rule, except that the tokens used to name the template parameters may differ as long as a token used to name a template parameter in one expression is replaced by another token that names the same template parameter in the other expression.
Two unevaluated operands that do not involve template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one-definition rule, except that the tokens used to name types and declarations may differ as long as they name the same entities, and the tokens used to form concept-ids may differ as long as the two template-ids are the same ([temp.type]).
Two potentially-evaluated expressions involving template parameters that are not equivalent are functionally equivalent if, for any given set of template arguments, the evaluation of the expression results in the same value.
Two unevaluated operands that are not equivalent are functionally equivalent if, for any given set of template arguments, the expressions perform the same operations in the same order with the same entities.
E.g. std::enable_if_t<std::is_same_v<T, T>> and void are only functionally equivalent: The first term will be evaluated to void for any template argument T. This means according to [temp.over.link]/7 code containing two specialisations <T, void> and <T, std::enable_if_t<std::is_same_v<T, T>> is already ill-formed:
If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required.
In the code above std::enable_if_t<std::is_same_v<T, int>> is not even functionally equivalent to any of the other versions as it is in general not equivalent to void.
When now performing partial ordering [temp.func.order] to see which specialisation matches best your call this will result in an ambiguity as test_class is equally specialised [temp.func.order]/6 in both cases (with either Ts={int}, void or T=int, std::enable_if_t<std::is_same_v<T, int>>, both resulting in int, void but you can't deduce them against each other) and therefore the compilation will fail.
On the other hand by wrapping std::enable_if_t with std::void_t, which is nothing more but an alias for void
template <typename T>
using void_t = void;
the partial ordering will succeed as in this case the compiler will already know the type of the last template parameter is void in all cases, choosing test_class<T, std::void_t<std::enable_if_t<std::is_same_v<T,int>>>> with T=int as the most specialised as the non-variadic case (T=int, void) is considered more specialised than the variadic one Ts={int}, void (see e.g. temp.deduct.partial/8 or again this).

Class template specialization priority/ambiguity

While trying to implement a few things relying on variadic templates, I stumbled accross something I cannot explain. I boiled down the problem to the following code snippet:
template <typename ... Args>
struct A {};
template <template <typename...> class Z, typename T>
struct test;
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
int main() {
test<A, A<int>>::foo();
}
Under gcc, it produces an error because it considers both specializations to be equally specialized when trying to instantiate test<A, A<int>>:
main.cpp: In function 'int main()':
main.cpp:25:24: error: ambiguous template instantiation for 'struct test<A, A<int> >'
test<A, A<int>>::foo();
^~
main.cpp:11:12: note: candidates are: template<template<class ...> class Z, class T> struct test<Z, Z<T> > [with Z = A; T = int]
struct test<Z, Z<T>> {
^~~~~~~~~~~~~
main.cpp:18:12: note: template<template<class ...> class Z, class T, class ... Args> struct test<Z, Z<T, Args ...> > [with Z = A; T = int; Args = {}]
struct test<Z, Z<T, Args...>> {
However, clang deems the first specialization "more specialized" (through partial ordering: see next section) as it compiles fine and prints:
I'm more specialized than the variadic spec, hehe!
A live demo can be found on Coliru. I also tried using gcc's HEAD version and got the same errors.
My question here is: since these two well-known compilers behave differently, which one is right and is this piece of code correct C++?
Standard interpretation (C++14 current draft)
From the sections §14.5.5.1 and $14.5.5.2 of the C++14 standard draft, partial ordering is triggered to determine which specialization should be chosen:
(1.2) — If more than one matching specialization is found, the partial order rules (14.5.5.2) are used to determine
whether one of the specializations is more specialized than the others. If none of the specializations
is more specialized than all of the other matching specializations, then the use of the class template is
ambiguous and the program is ill-formed.
Now according to §14.5.5.2, the class template specializations are transformed into function templates through this procedure:
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 (14.5.6.2):
(1.1) — the first function template has the same template parameters as the first partial specialization and has
a single function parameter whose type is a class template specialization with the template arguments
of the first partial specialization, and
(1.2) — the second function template has the same template parameters as the second partial specialization
and has a single function parameter whose type is a class template specialization with the template
arguments of the second partial specialization.
Therefore, I tried to reproduce the issue with the function template overloads that the transformation described above should generate:
template <typename T>
void foo(T const&) {
std::cout << "Generic template\n";
}
template <template <typename ...> class Z, typename T>
void foo(Z<T> const&) {
std::cout << "Z<T>: most specialized overload for foo\n";
}
template <template <typename ...> class Z, typename T, typename ... Args>
void foo(Z<T, Args...> const&) {
std::cout << "Z<T, Args...>: variadic overload\n";
}
Now trying to use it like this:
template <typename ... Args>
struct A {};
int main() {
A<int> a;
foo(a);
}
yields a compilation error [ambiguous call] in both clang and gcc: live demo. I expected clang would at least have a behavior consistent with the class template case.
Then, this is my interpretation of the standard (which I seem to share with #Danh), so at this point we need a language-lawyer to confirm this.
Note: I browsed a bit LLVM's bug tracker and could not find a ticket for the behavior observed on function templates overloads in this question.
From temp.class.order:
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 ([temp.func.order]):
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.
The order of:
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I'm more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I'm variadic!" << std::endl;
}
};
depends on the order of:
template <template <typename...> class Z, typename T>
void bar(test<Z, Z<T>>); // #1
template <template <typename...> class Z, typename T, typename ... Args>
void bar(test<Z, Z<T, Args...>>); // #2
From [temp.func.order]:
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.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs ([temp.variadic]) 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].
By those paragraph, for any function transformed from any synthesized template Z0 and type T0, which can form #1, we can do type deduction with #2. But functions transformed from #2 with fictitious template Z2 with any type T2 and any non-empty set of Args2 can't be deduced from #1. #1 is obviously more specialized than #2.
clang++ is right in this case.
Actually, this one and this one are failed to compile (because of ambiguous) in both g++ and clang. It seems like both compilers have hard time with template template parameters. (The latter one is clearly ordered because its order is the same of no function call).

SFINAE(enable_if) on template arguments without variable specifier

This is the code:
#include <iostream>
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
is_odd (T i) {return bool(i%2);}
// 2. the second template argument is only valid if T is an integral type:
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}
int main() {
short int i = 1; // code does not compile if type of i is not integral
std::cout << std::boolalpha;
std::cout << "i is odd: " << is_odd(i) << std::endl;
std::cout << "i is even: " << is_even(i) << std::endl;
return 0;
}
I am trying to learn proper usage of enable_if, i understand if it's used as a return type specifier is that: the compiler will ignore the code. Meaning, the function won't be in the binary file.
I am somehow confused if it's used in a template argument. Based from the code above, it says there that the second template argument is only valid if T is an integral type But i am confused what is the purpose of that second argument?
I removed it and changed it to:
template < class T>
bool is_even (T i) {return !bool(i%2);}
and it still works fine. Can someone clarify me what's the real purpose of it? There's no variable specifier on it as well.
Or maybe it only serves as a checker if ill do something like
template < class T,
class B= typename std::enable_if<std::is_integral<T>::value>::type>
Allowing me to access B on my code(can be true or false) ?
You are using it with an integral type. It will only fail when used with a non-integral type. The following should fail to compile:
float f;
is_even(f);
Note that with a C++14 compiler you can write:
template <class T,
class = std::enable_if_t<std::is_integral<T>::value>>
However, you may want to use a static_assert here, since the function probably does not make sense for non integral types, and it will give a better error message at compile time.
Another usage of additional parameters in SFINAE is to lower the preference for a given function. The template with more template arguments with be less likely to get selected than the one with less templates arguments.
The purpose of the enable_if in this case is to cause a compilation error when the deduced template argument for T is not an integral type. We can see from cppreference/is_integral that integral types are integers, characters and their signed and unsigned variants.
For any other type you will get an error that looks like:
main.cpp:21:32: error: no matching function for call to 'is_odd'
std::cout << "i is odd: " << is_odd(NotIntegral()) << std::endl;
^~~~~~
main.cpp:6:25: note: candidate template ignored: disabled by 'enable_if' [with T = NotIntegral]
typename std::enable_if<std::is_integral<T>::value,bool>::type
I understand if it's used as a return type specifier is that the compiler will ignore the code
This isn't true. The return type is evaluated just like any other part of the declaration. See Why should I avoid std::enable_if in function signatures. The choice of placement of the std::enable_if has its pros and cons.
But i am confused what is the purpose of that second argument?
Consider the example where you have a function named foo that takes some T.
template<class T> void foo(T);
You want to restrict one overload of foo whose T's have a value member equal to 1, and want to use another overload for when T::value does not equal 1. If we simply have this:
template<class T> void foo(T); // overload for T::value == 1
template<class T> void foo(T); // overload for T::value != 1
How does this convey to the compiler that you want to use two separate overloads for two separate things? It doesn't. They are both ambiguous function calls:
template<std::size_t N>
struct Widget : std::integral_constant<std::size_t, N> { };
int main() {
Widget<1> w1;
Widget<2> w2;
foo(w1); // Error! Ambiguous!
foo(w2); // Error! Ambiguous!
}
You will need to use SFINAE to reject the templates based on our condition:
template<class T, class = std::enable_if_t<T::value == 1>* = nullptr>
void foo(T); // #1
template<class T, class = std::enable_if_t<T::value != 1>* = nullptr>
void foo(T); // #2
Now the correct ones are called:
foo(w1); // OK! Chooses #1
foo(w2); // OK! Chooses #2
What makes this work is the way std::enable_if is built. If the condition in its first parameter is true, it provides a member typedef named type equal to the second parameter (defaulted to void). Otherwise, if the condition is false, it does not provided one. A reasonable implementation could be:
template<bool, class R = void>
struct enable_if { using type = R; };
template<class R>
struct enable_if<false, R> { /* empty */ };
So if the condition fails, we will be trying to access a ::type member that doesn't exist (remember that std::enable_if_t is an alias for std::enable_if<...>::type). The code would be ill-formed, but instead of it causing a hard error, during overload resolution the template is simply rejected from the candidate set and other overloads (if present) are used instead.
This is a simple explanation of SFINAE. See the cppreference page for more information.
In your case there is only one overload of is_odd and is_even, so if you pass NotIntegral to them, they will fail because their respective overloads are taken out of the candidate set, and since there are no more overloads to evaluate overload resolution fails with the aforementioned error. This could be cleaned up a bit with a static_assert message instead.
template<class T>
bool is_even (T i) {
static_assert(std::is_integral<T>::value, "T must be an integral type");
return !bool(i%2);
}
Now you will no longer be given a weird looking error message but your own custom one. This also rules out SFINAE since the static_assert is evaluated after template arguments are substituted, so it's your choice on which route to take. I would recommend the assertion since it's much cleaner and there is no need for SFINAE here.