template class specialization enable_if and default values - c++

Given the following
template <typename T, typename Enable=void>
struct test{};
template <typename T,
typename std::enable_if< some_trait<T>::value >::type >
struct test{};
assuming some_trait<T>::value is true, enable_if<T>::type is void, and the specialization is selected.
However, my query is related to the selection when the follow is the case.
template <typename T,
typename std::enable_if_t< some_trait<T>::value,T>::type >
struct test{};
When a second non void template parameter for enable_if is provided for ::type, the unspecialized template
is selected, even when some_trait<T>::value is true, as ::type is T instead of void, and so doesn't
match the default value in the primary template.
My question is where in the standard is the ordering described for which template is chosen, and why is
the instanciation test<T,void> considered as a better match then test<T,T>.
https://ideone.com/7v4TTS
full sample :
#include <iostream>
#include <type_traits>
template <typename T,typename Enable=void>
struct test
{
const char* value = "Primary";
};
#if 1// toggle this
template <typename T>
struct test<T,typename std::enable_if< std::is_same<T,T>::value >::type >
{
const char* value = "Specialization";
};
#else
template <typename T>
struct test<T,typename std::enable_if< std::is_same<T,T>::value,T >::type>
{ /// ^
const char* value = "Specialization";
};
#endif
int main() {
test<int> v;
std::cout << v.value << std::endl;
return 0;
}

In the first case you have the type
std::enable_if< true, void >
In the second case you have the type
std::enable_if< true, int >
std::enable_if< true, int >::type is int. It doesnt qualify a as type for typename Enable, which was requested to be void

test<int> v; is test<int, void> v; (thanks to default type).
test<int, int> v; would select your last specialization.

My question is where in the standard is the ordering described for which template is chosen, and why is the instanciation test<T,void> considered as a better match then test<T,T>.
[temp.class.spec.match].
You wrote test<int>, which means that you didn't provide any template argument for the second parameter. Because it has a default parameter, it is chosen, so you actually have test<int, void>.
Now, according to the text linked above, the template parameters are matched to a specialization.
In the first case, the specialization is test<int, void> after evaluation, and so it is an exact match and chosen.
In the second case, the specialization is test<int, int> after evaluation, which is not an exact match and so the primary template is chosen instead of that specialization.

Related

SFINAE template specialization matching rule

I'm learning about SFINE with class/struct template specialization and I'm a bit confused by the matching rule in a nuanced example.
#include <iostream>
template <typename T, typename = void>
struct Foo{
void foo(T t)
{
std::cout << "general";
}
};
template <typename T>
struct Foo<T, typename std::enable_if<std::is_integral<T>::value>::type>
{
void foo(T t)
{
std::cout << "specialized";
}
};
int main()
{
Foo<int>().foo(3);
return 0;
}
This behaves as expected and prints out specialized, however if I change the specialized function slightly to the following:
template <typename T>
struct Foo<T, typename std::enable_if<std::is_integral<T>::value, int>::type>
{
void foo(T t)
{
std::cout << "specialized";
}
};
then it prints out general. What's changed in the second implementation that makes it less 'specialized'?
Re-focus your eyeballs a few lines higher, to this part:
template <typename T, typename = void>
struct Foo{
This means that when this template gets invoked here:
Foo<int>().foo(3);
This ends up invoking the following template: Foo<int, void>. After all, that's what the 2nd template parameter is, by default.
The 2nd template parameter is not some trifle, minor detail that gets swept under the rug. When invoking a template all of its parameters must be specified or deduced. And if not, if they have a default value, that rescues the day.
SFINAE frequently takes advantage of default template parameters. Now, let's revisit your proposed template revision:
template <typename T>
struct Foo<T, typename std::enable_if<std::is_integral<T>::value, int>::type>
The effect of this specialization is that the 2nd template parameter in the specialization is int, not void.
But Foo<int> is going to still use the default void for the 2nd template parameter because, well, that's the default value of the 2nd template parameter. So, only the general definition will match, and not any specialization.
It's not that it's less specialized, it no longer matches the template instantiation. The second type parameter defaults to void, but since the std::enable_if now aliases int, the specialization no longer matches.

Template specialization on template member of template class

This is probably only a syntax problem.
So i have this template class :
template <typename String, template<class> class Allocator>
class basic_data_object
{
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
};
And another one :
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
};
Now i want to specialize the second one's T parameter with the first one's inner typedef array_container for any given type.
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::template array_container<T>>
{
};
But this specialization doesn't seem to be matched when i pass an std::vector as the last parameter.
If i create a temporary hard coded typedef:
typedef basic_data_object<std::string, std::allocator<std::string>> data_object;
And use it for the specialization, everything works :
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};
What did i miss ? :)
Alternatively what is the best (smallest / cleanest) way to make this work ?
The C++ standard says, in [temp.class.spec.match] paragraph 2:
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 (14.8.2).
14.8.2 is [temp.arg.deduct] i.e. the clause describing template argument deduction for function templates.
If you modify your code to use a similar function template and attempt to call it, you will see that the arguments cannot be deduced:
template <typename String, typename T>
void deduction_test(String,
typename basic_data_object<String, std::allocator>::template array_container<T>)
{ }
int main()
{
deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
}
(I removed the Allocator parameter, since there's no way to pass a template template parameter as a function argument and in the basic_data_object type it's a non-deduced context, I don't believe it affects the result.)
Both clang and GCC say they cannot deduce T here. Therefore the partial specialization will not match the same types used as template arguments.
So I haven't really answered the question yet, only clarified that the reason is in the rules of template argument deduction, and shown an equivalence with deduction in function templates.
In 14.8.2.5 [temp.deduct.type] we get a list of non-deduced contexts that prevent deduction, and the following rule in paragraph 6:
When a type name is specified in a way that includes a non-deduced context, all of the types that comprise that type name are also non-deduced.
Since basic_data_object<String, Allocator> is in a non-deduced context (it is a nested-name-specifier, i.e. appears before ::) that means the type T is also non-deduced, which is exactly what Clang and GCC tell us.
With your temporary hardcoded typedef there is no non-deduced context, and so deduction for T succeeds using the deduction_test function template:
template <typename String, typename T>
void deduction_test(String,
typename data_object::template array_container<T>)
{ }
int main()
{
deduction_test(std::string{}, std::vector<int, std::allocator<int>>{}); // OK
}
And so, correspondingly, your class template partial specialization can be matched when it uses that type.
I don't see a way to make it work without changing the definition of get_data_object_value, but if that's an option you can remove the need to deduce the array_container type and instead use a trait to detect whether a type is the type you want, and specialize on the result of the trait:
#include <string>
#include <vector>
#include <iostream>
template <typename String, template<class> class Allocator>
class basic_data_object
{
public:
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
template<typename T>
struct is_ac : std::false_type { };
template<typename T>
struct is_ac<array_container<T>> : std::true_type { };
};
template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
struct get_data_object_value
{
};
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value<String, Allocator, T, true>
{
void f() { }
};
int main()
{
get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
obj.f();
}
This doesn't really scale if you wanted several class template partial specializations, as you would need to add several bool template parameters with default arguments.
For some reason, the problem seems to stem from the double level of templates. I'll leave you check the 3 test cases below, they are simple:
Remove the template arguments of First: works as expected
Make First a template, but the inner type a plain one: works as expected
Make both First and the inner type templates: compiles but the output is unexpected
Note: the template template parameter Allocator is useless to reproduce the issue, so I left it out.
Note: both GCC (ideone's version, 4.8.1 I believe) and Clang (Coliru version, 3.4) compile the code, and yet produce the same baffling result
From the 3 above examples, I deduce:
that this is NOT a non-deducible context issue; otherwise why would (2) work ?
that this is NOT an alias issue; otherwise why would (1) work ?
And therefore that either the problem is much hairier than the current hints would make us believe OR that both gcc and Clang have a bug.
EDIT: Thanks to Jonathan Wakely who patiently educated me enough that I could finally understand both the Standard wording related to this case and how it applied. I will now attempt to explain this (again) in my own words. Please refer to Jonathan's answer for the exact Standard quotes (it all sits in [temp.deduct.type])
When deducing template parameters (Pi), whether for functions or classes, the deduction is done independently for each and every argument.
Each argument need provide zero or one candidate Ci for each parameter; if an argument would provide more than one candidate, it provides none instead.
Thus, each argument produces a dictionary Dn: Pi -> Ci which maps a subset (possibly empty) of the template parameters to be deduced to their candidate.
The dictionaries Dn are merged together, parameter by parameter:
if only one dictionary has a candidate for a given parameter, then this parameter is accepted, with this candidate
if several dictionaries have the same candidate for a given parameter, then this parameter is accepted, with this candidate
if several dictionaries have different incompatible (*) candidates for a given parameter, then this parameter is rejected
If the final dictionary is complete (maps each and every parameter to an accepted candidate) then deduction succeeds, otherwise it fails
(*) there seems to be a possibility for finding a "common type" from the available candidates... it is of no consequence here though.
Now we can apply this to the previous examples:
1) A single template parameter T exists:
pattern matching std::vector<int> against typename First::template ArrayType<T> (which is std::vector<T>), we get D0: { T -> int }
merging the only dictionary yields { T -> int }, thus T is deduced to be int
2) A single template parameter String exists
pattern matching std::vector<int> against String, we get D0: { String -> std::vector<int> }
pattern matching std::vector<int> against typename First<String>::ArrayType we hit a non-deducible context (many values of String could fit), we get D1: {}
merging the two dictionaries yields { String -> std::vector<int> }, thus String is deduced to be std::vector<int>
3) Two template parameters String and T exist
pattern matching std::vector<char> against String, we get D0: { String -> std::vector<char> }
pattern matching std::vector<int> against typename First<String>::template ArrayType<T> we hit a non-deducible context, we get D1: {}
merging the two dictionaries yields { String -> std::vector<char> }, which is an incomplete dictionary (T is absent) deduction fails
I must admit I had not considered yet that the arguments were resolved independently from one another, and therefore than in this last case, when computing D1 the compiler could not take advantage of the fact that D0 had already deduced a value for String. Why it is done in this fashion, however, is probably a full question of its own.
Without the outer template, it works, as in it prints "Specialized":
#include <iostream>
#include <vector>
struct First {
template <typename T>
using ArrayType = std::vector<T>;
};
template <typename T>
struct Second {
void go() { std::cout << "General\n"; }
};
template <typename T>
struct Second < typename First::template ArrayType<T> > {
void go() { std::cout << "Specialized\n"; }
};
int main() {
Second < std::vector<int> > second;
second.go();
return 0;
}
Without the inner template, it works, as in it prints "Specialized":
#include <iostream>
#include <vector>
template <typename String>
struct First {
using ArrayType = std::vector<int>;
};
template <typename String, typename T>
struct Second {
void go() { std::cout << "General\n"; }
};
template <typename String>
struct Second < String, typename First<String>::ArrayType > {
void go() { std::cout << "Specialized\n"; }
};
int main() {
Second < std::vector<int>, std::vector<int> > second;
second.go();
return 0;
}
With both, it fails, as in it prints "General":
#include <iostream>
#include <vector>
template <typename String>
struct First {
template <typename T>
using ArrayType = std::vector<T>;
};
template <typename String, typename T>
struct Second {
void go() { std::cout << "General\n"; }
};
template <typename String, typename T>
struct Second < String, typename First<String>::template ArrayType<T> > {
void go() { std::cout << "Specialized\n"; }
};
int main() {
Second < std::vector<char>, std::vector<int> > second;
second.go();
return 0;
}
The answer of Jonathan Wakely gives the reason why your code does not work.
My answer shows you how to solve the problem.
In your example, the container type over which you want to specialize is defined outside of basic_data_object thus you can of course use it directly in your specialization:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,std::vector<T,A>>
{ };
This definitely conforms with the standard and works with all compilers.
In the case where the type is defined in basic_data_object, you can move it out of the class.
Example: Instead of
template<typename S, template<class> class A>
struct a_data_object
{
template<typename T>
struct a_container
{ };
};
write this:
template<typename S, template<class> class A, typename T>
// you can perhaps drop S and A if not needed...
struct a_container
{ };
template<typename S, template<class> class A, typename T>
struct a_data_object
{
// use a_container<S,A,T>
};
Now you can specialize with:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,a_container<S,A,T>>
{ };
Note: The next "solution" is apparently a bug with GCC 4.8.1.
If the container is only defined in an enclosing template and can not be moved out you can do this:
Get the container type out of basic_data_object:
template<typename S, template<class> class A, typename T>
using bdo_container = basic_data_object<S,A>::array_container<T>;
Write a specialization for this type:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,bdo_container<S,A,T>>
{ };
Alternatively what is the best (smallest / cleanest) way to make this work?
Arguably, it is:
Write a SFINAE trait template Tr<String,Allocator,T> that determines whether T is the
same as basic_data_object<String,Allocator>::array_container<T::E>
for some type E - if such there be - that is T::value_type.
Provide template get_data_object_value with a 4th parameter
defaulting to Tr<String,Allocator,T>::value
Write partial specializations of get_data_object_value instantiating that
4th parameter as true, false respectively.
Here is a demo program:
#include <type_traits>
#include <vector>
#include <iostream>
template <typename String, template<class> class Allocator>
struct basic_data_object
{
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
};
template<typename T, typename String, template<class> class Allocator>
struct is_basic_data_object_array_container
/*
A trait template that has a `static const bool` member `value` equal to
`true` if and only if parameter type `T` is a container type
with `value_type E` s.t.
`T` = `basic_data_object<String,Allocator>::array_container<T::E>`
*/
{
template<typename A>
static constexpr bool
test(std::is_same<
A,
typename basic_data_object<String,Allocator>::template
array_container<typename A::value_type>
> *) {
return std::is_same<
A,
typename basic_data_object<String,Allocator>::template
array_container<typename A::value_type>
>::value;
}
template<typename A>
static constexpr bool test(...) {
return false;
}
static const bool value = test<T>(nullptr);
};
template <
typename String,
template<class> class Allocator,
typename T,
bool Select =
is_basic_data_object_array_container<T,String,Allocator>::value
>
struct get_data_object_value;
template <
typename String,
template<class> class Allocator,
typename T
>
struct get_data_object_value<
String,
Allocator,
T,
false
>
{
static void demo() {
std::cout << "Is NOT a basic_data_object array_container" << std::endl;
}
};
template <
typename String,
template<class> class Allocator,
typename T>
struct get_data_object_value<
String,
Allocator,
T,
true
>
{
static void demo() {
std::cout << "Is a basic_data_object array_container" << std::endl;
}
};
#include <list>
#include <memory>
using namespace std;
int main(int argc, char **argv)
{
get_data_object_value<string,allocator,std::vector<short>>::demo();
get_data_object_value<string,allocator,std::list<short>>::demo();
get_data_object_value<string,allocator,short>::demo();
return 0;
}
Built with gcc 4.8.2, clang 3.4. Output:
Is a basic_data_object array_container
Is NOT a basic_data_object array_container
Is NOT a basic_data_object array_container
VC++ 2013 will not compile this for lack of constexpr support. To accommodate that
compiler the following less natural implementation of the trait may be used:
template<typename T, typename String, template<class> class Allocator>
struct is_basic_data_object_array_container
{
template<typename A>
static
auto test(
std::is_same<
A,
typename basic_data_object<String, Allocator>::template
array_container<typename A::value_type>
> *
) ->
std::integral_constant<
bool,
std::is_same<
A,
typename basic_data_object<String, Allocator>::template
array_container<typename A::value_type>
>::value
>{}
template<typename A>
static std::false_type test(...);
using type = decltype(test<T>(nullptr));
static const bool value = type::value;
};
Edit: This answer only works because of a bug in GCC 4.8.1
Your code works as expected if you drop the keyword template in your specialization:
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
void foo() { std::cout << "general" << std::endl; }
};
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::array_container<T>>
// ^^^^^^ no template!
{
void foo() { std::cout << "special" << std::endl; }
};
Example tested with GCC 4.8.1:
int main() {
get_data_object_value<std::string,std::allocator,std::vector<int>> obj;
obj.foo(); // prints "special"
}

template specialization with default parameters

The following code compiles. Anyone can explain why? I've been digging the standard to figure out why it's legal.
template <bool B, typename T = void> struct enable_if { };
template <typename T> struct enable_if<true, T> {
typedef T type;
};
template <typename T, typename Enable = void> struct A;
template <typename T> struct A<T, typename enable_if<(sizeof(T) <= ~0ULL)>::type> {
void f() { }
};
int main() {
A<int> a;
a.f();
}
At the statement:
A<int> a;
As there's only one template paramerter "int", the compiler should go to use the primary template, which is:
template <typename T, typename Enable = void> struct A;
which is undefined, thus causing an error.
From § 14.5.5.1
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.
— If exactly one matching specialization is found, the instantiation is generated from
that specialization.
Let's try to figure out what's going on here:
// definition of enable_if, second parameter is defaulted to void
template <bool B, typename T = void>
struct enable_if { };
// specialization of enable_if, if first parameter is true,
// enable_if has a typedef for the second parameter
template <typename T>
struct enable_if<true, T> {
typedef T type;
};
// definition of struct A, second parameter defaults to void
template <typename T, typename Enable = void>
struct A;
// specialization of struct A, second parameter
// is obtained from the enable_if::type typedef
// the first parameter of enable_if is true if the size of T
// is smaller than the max long long (~0 --> all F)
template <typename T>
struct A<T, typename enable_if<(sizeof(T) <= ~0ULL)>::type> {
void f() { }
};
int main() {
// So we attempt the specialization for struct A<int,enable_if...>
// The expression of enable_if evaluates to...
// (sizeof(int) <= ~0ULL) == true
// ... so it applies the specialization of enable_if<true,void>
// (second parameter is void because none is provided, so it
// uses the default.
// so the enable_if template is valid (selected the specialization)
// and that means that the struct A<int,enable_if> specialization
// is valid too, so it is selected.
A<int> a;
a.f();
}
The compiler uses the template A<int, enable_if<true>:::type > when you declare A<int> since sizeof(int) <= ~0ULL evaluates to true.
There is no problem with enable_if<true>::type because the compiler is able to use enable_if<true, true>::type.
When you consider your enable_if:
template <bool B, typename T = void> struct enable_if{ };
template <typename T> struct enable_if<true, T>
{
typedef T type;
};
in
void test_EnableIf
{
static_assert(
std::is_same<
enable_if<(sizeof(int) > 0)>::type,
void>::value, "test_EnableIf failed." );
}
the result (type) is void, as no type was specified (as second
template parameter). The specialization of enable_if is selected
because of the boolean expression being true, and the default
parameter is selected (from primary template) because no other was
provided, and hence type is void, but NOTE that the definition
of type does exist (as the specialization was selected).
Now, in your definition of A...
template <typename T, typename Enable = void> struct A;
template <typename T>
struct A<T, typename enable_if<
(sizeof(T) <= ~0ULL)>::type>
{
void f() { }
};
...because type does exist in enable_if, it is a better match, which causes the specialization to be selected, and hence compiles.
A trivial example which amounts to the same thing is the following:
template <class T, class U = void>
struct X;
template <class T>
struct X<T,void>
{
static int foo(){ return 0; }
};
int main()
{
return X<int>::foo();
}

Template specialization to use default type if class member typedef does not exist

I'm trying to write code that uses a member typedef of a template argument, but want to supply a default type if the template argument does not have that typedef. A simplified example I've tried is this:
struct DefaultType { DefaultType() { printf("Default "); } };
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } };
struct A {};
struct B { typedef NonDefaultType Type; };
template<typename T, typename Enable = void> struct Get_Type {
typedef DefaultType Type;
};
template<typename T> struct Get_Type< T, typename T::Type > {
typedef typename T::Type Type;
};
int main()
{
Get_Type<A>::Type test1;
Get_Type<B>::Type test2;
}
I would expect this to print "Default NonDefault", but instead it prints "Default Default". My expectation is that the second line in main() should match the specialized version of Get_Type, because B::Type exists. However, this does not happen.
Can anyone explain what's going on here and how to fix it, or another way to accomplish the same goal?
Thank you.
Edit:
Georg gave an alternate method, but I'm still curious about why this doesn't work. According the the boost enable_if docs, a way to specialize a template for different types is like so:
template <class T, class Enable = void>
class A { ... };
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };
template <class T>
class A<T, typename enable_if<is_float<T> >::type> { ... };
This works because enable_if< true > has type as a typedef, but enable_if< false > does not.
I don't understand how this is different than my version, where instead of using enable_if I'm just using T::Type directly. If T::Type exists wouldn't that be the same as enable_if< true >::type in the above example and cause the specialization to be chosen? And if T::Type doesn't exist, wouldn't that be the same as enable_if< false >::type not existing and causing the default version to be chosen in the above example?
To answer your addition - your specialization argument passes the member typedef and expects it to yield void as type. There is nothing magic about this - it just uses a default argument. Let's see how it works. If you say Get_Type<Foo>::type, the compiler uses the default argument of Enable, which is void, and the type name becomes Get_Type<Foo, void>::type. Now, the compiler checks whether any partial specialization matches.
Your partial specialization's argument list <T, typename T::Type> is deduced from the original argument list <Foo, void>. This will deduce T to Foo and afterwards substitutes that Foo into the second argument of the specialization, yielding a final result of <Foo, NonDefaultType> for your partial specialization. That doesn't, however, match the original argument list <Foo, void> at all!
You need a way to yield the void type, as in the following:
template<typename T>
struct tovoid { typedef void type; };
template<typename T, typename Enable = void> struct Get_Type {
typedef DefaultType Type;
};
template<typename T>
struct Get_Type< T, typename tovoid<typename T::Type>::type > {
typedef typename T::Type Type;
};
Now this will work like you expect. Using MPL, you can use always instead of tovoid
typename apply< always<void>, typename T::type >::type
You can do that by utilizing SFINAE:
template<class T> struct has_type {
template<class U> static char (&test(typename U::Type const*))[1];
template<class U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<class T, bool has = has_type<T>::value> struct Get_Type {
typedef DefaultType Type;
};
template<class T> struct Get_Type<T, true> {
typedef typename T::Type Type;
};
First step: stop using "Type" and use the mpl standard "type".
BOOST_MPL_HAS_XXX_DEF(Type)
template < typename T >
struct get_type { typedef typename T::Type type; };
template < typename T >
struct calculate_type : boost::mpl::if_
<
has_Type<T>
, get_type<T>
, boost::mpl::identity<default_type>
>::type {}
typedef calculate_type<A>::type whatever;
If you used "type" instead of "Type" in your metafunctions you wouldn't require the fetcher "get_type" to convert it and could just return T in that case.

Partial Specialization Question

I need a fresh pair of eyes.
This is obviously illegal, but it shows what I'm trying to do:
template <typename T, T> struct Foo
{
};
template <typename T> struct Foo <T, 0> //Obviously I can't do this.
{
};
Is there any way to wrap T or do something tricky so that this sort of thing can work?
Thanks!
Yes, you can use this trick:
template <typename T, T, T=0> struct Foo {
};
template <typename T, T t> struct Foo <T, t, t> {
};
If t is 0 in the specialization, it will match the default argument, and the specialization is taken. Otherwise, the primary template is taken.
Edit: What the heck does the third parameter mean? Well, it's a default and it's 0. It will be passed when we name the specialization Foo<int, 5> for example. But really, we instantiate a template with the arguments Foo<int, 5, 0>, because the last is a default argument. The partial specialization matches, when the third parameter matches the third argument, which is zero by default, and if the third and second arguments are the same, because both are t.
The above trick has the drawback that also Foo<int, 9, 9> uses our specialization. But on the other side, the above is remarkable simple, so that you can probably get away with that. If you don't want that to work, then you can use enable_if, which is a bit more complicated:
template <typename T, T, typename = void> struct Foo {
};
template <typename T, T t>
struct Foo <T, t, typename boost::enable_if_c< t == 0 >::type> {
};
Now, even if you say Foo<int, 9, void>, our partial specialization won't be chosen, because the condition t == 0 isn't true, and ::type will thus not be available. SFINAE doesn't chose the specialization then. Of course, with this enable_if solution, you are not limited to t being zero. Any condition will do. For reference, here is the code of enable_if, if you don't use boost. Cut the _c suffix above then, which we don't need for the version below:
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };