Partial Specialization Question - c++

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> { };

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.

Partial Specialization using a qualified name

I'm trying to partially specialize a template for a metafunction and ran into a problem.
I specialized the template like this:
template <typename A, typename B>
struct Foo;
template <typename A, typename B1>
struct Foo<A, typename A::template Bar<B1>> {
/* use both A and B1*/
};
template <typename A, typename B1>
struct Foo<A, typename A::template Xyz<B1>> {
/* use both A and B1*/
};
However this results (Visual Studio 2019) in
Error C2764: 'B1': template parameter not used or deducible in partial specialization 'Foo<A,A::Bar<B1>>' (5, 47)
I assume this is because I used the template parameter A as a qualifier in the specialication (typename A::template Bar<B1>).
Is there any way to circumvent this and use parameters in template specializations as qualifiers?
Note: In my usecase the first parameter is never really specialized.
Theoretically it could work to nest the specialized template in another template class (i.e. currying the metafunction), but templates can only be specialized at namespace scope.
Using a template template parameter may work out:
template <typename A, typename B>
struct Foo;
template <typename TA, template<class> class TBar, typename B1>
struct Foo<TA, TBar<B1>> {};
Given
struct A
{
template<class T>
struct Bar {};
};
you can form
Foo<A, A::Bar<int>> x;
and it will deduce A, A::Bar and int in the specialization for you. But note that no attempt is made to check that the A in A::Bar matches the A given as first template parameter; it's unclear what you'd expect to happen for, say, a Foo<double, A::Bar<int>>.
https://godbolt.org/z/hGhsZm
I assume this is because I used the template parameter A as a qualifier in the specialication (typename A::template Bar).
I don't think so.
Suppose A is as follows
struct A
{
template <typename B>
using Bar = int;
};
and that you define a Foo<A,A::Bar<B1>>.
But A::Bar<B1> is int !
So you're defining Foo<A, int>.
How can, the compiler, deduce B1 from int ?
It seems to me that it can't.
Possible solution (depending from your needs): if you need to specialize through B1, but you need A::Bar<B1> inside Foo, you can use B1 itself as second parameter and A::Bar<B1> as using type inside Foo
template <typename A, typename B1>
struct Foo<A, B1> {
using bType = A::template Bar<B1>;
};

How to unroll a parameter pack from right to left

I am trying to do a parameter pack unrolling by dispatching to a class recursively. I'd like to do that from right to left since some of the operations do pre-pending.
template <typename... T>
class Foo;
template <typename T>
class Foo<T> {/* base case implementation*/};
template <typename T, typename R, typename... Rs>
class Foo<T, Rs..., R> {
private:
Foo<T, Rs...> foo_;
}
Unfortunately, the above gets me:
class template partial specialization contains template parameters that cannot be deduced;
this partial specialization will never be used
This seems odd to me, I would assume that even though the arguments has switched order, Foo<T, Rs..., R> should still match the template specialization.
I've looked at some similar questions:
Specifically, C++ template partial specialization: Why cant I match the last type in variadic-template?
However, the highest voted (non-accepted) answer doesn't make sense to me. Sure I understand that the template parameter pack declaration must be the last in the declaration, but I do that for the template specialization.
I'm not sure why the compiler cannot map Foo<T, Rs..., R> to the initial template declaration Foo<T...> and enforces the parameter pack declaration order there.
Other answers on that thread offer how to extract the last value, but that still doesn't allow you to do recursive parameter pack unrolling, which is sort of the whole point here. Is it just impossible to unroll a parameter pack from right to left?
Here is an utility to instatiate a template with a reverse order of template parameters:
#include <type_traits>
#include <tuple>
template <template <typename...> typename Template, typename ...Arg>
struct RevertHelper;
template <template <typename > typename Template, typename Arg>
struct RevertHelper<Template, Arg>
{
using Result = Template<Arg>;
};
template <template <typename... > typename Template, typename Head, typename ...Tail>
struct RevertHelper<Template, Head, Tail...>
{
private:
template <typename ...XArgs>
using BindToTail = Template<XArgs..., Head>;
public:
using Result = typename RevertHelper<BindToTail, Tail...>::Result;
};
static_assert(std::is_same_v<typename RevertHelper<std::tuple, int, double>::Result, std::tuple<double, int>>, "");
So if you need to instantiate Foo with template pack Args... being reversed you can use
typename RevertHelper<Foo, Args...>::Result
To do the parameter pack expansion the way you want, dispatch to the reversed implementation:
namespace internal {
template <typename... T>
class FooHelper;
template <typename T>
class FooHelper<T> {/* base implementation */}
template <typename L, typename R, typename... Rs>
class FooHelper<T> {
private:
Foo<T, Rs...> foo_helper_;
};
}
template <typename... T>
class Foo {
typename RevertHelper<internal::FooHelper, T...>::Result foo_helper_;
};
I'm not sure why the compiler cannot map Foo<T, Rs..., R> to the initial template declaration Foo<T...> and enforces the parameter pack declaration order there.
Because partial ordering is already a really complex algorithm and adding extra complexity to that is fraught with peril. There was a proposal to make this work, which had this example:
template <class A, class... B, class C> void foo(A a, B... b, C c);
foo(1, 2, 3, 4); // b is deduced as [2, 3]
Straightforward enough right? Now, what if C has a default argument? What does this do:
template <class A, class... B, class C=int> void foo(A a, B... b, C c=5);
foo(1, 2, 3, 4);
There are two interpretations of this:
b is deduced as the pack {2, 3} and c is deduced as 4
b is deduced as the pack {2, 3, 4} and c is deduced as 5
Which is intended? Or do we just disallow default arguments after a function parameter pack?
Unfortunately, we have no nice pack indexing mechanism. In the meantime, just use Boost.Mp11:
template <typename... T>
class Foo;
template <typename T>
class Foo<T> {/* base case implementation*/};
template <typename T, typename... Rs>
class Foo<T, Rs...> {
private:
using R = mp_back<Foo>;
mp_pop_back<Foo> foo_;
};
Pattern matching in C++ template patterns is intentionally simplified for sake of simplicity of algorithm and understanding.
Take a look at hypothetical algorithm if this could be possible:
Get some declaration: using X = Foo<int, char, bool, double>;
Compiler checks specializations: first one is Foo - it's dropped.
Compiler checks specializations: second one is your Foo<T, Rs..., R>
T is int, we're fine.
R's can be emtpy, let's try to skip it.
R is char, but we're at the end of specialization parameters, let's get back to 2.
R's is char
R is bool, but we're at the end of specialization parameters, let's get back to 2.
R's is char, bool
R is double, we're fine, select this one
But this is only one scenario: another one would be to eat all parameters to the end and cut off one by one in order to try to match it. This can be problematic, because such template specialization would be inherently ambiguous with another possible specialization that doesn't seem to be an ambiguity here:
template<typename T, typename S>
class Foo<T, S> {};

How the when<> trait in boost.Hana works?

I have some experience with std::enable_if. IIRC, it is about if a well-formed expression results in true return back the user type T (if given) or void via nested type alias.
template<bool,typename = void>
struct enable_if;
template<typename T>
struct enable_if<true,T>{
using type = T;
};
template <typename T, typename = void>
struct base_template{
enum { value= false};
};
template <typename T>
struct base_template<T, typename enable_if<std::is_integral<T>::value>::type> {
enum { value= true};// something useful...
};
struct some{};
static_assert(base_template<some>::value,"F*"); //error: static assertion failed: F
static_assert(base_template<int>::value,"F*");
But in boost.Hana I see this trait when<> and its implementation is like
template <bool condition>
struct when;
template <typename T, typename = when<true>>
struct base_template{
enum { value= false};
};
template <typename T>
struct base_template<T, when<std::is_integral<T>::value>> {
enum { value= true};// something useful...
};
struct some{};
static_assert(base_template<int>::value,"F*");
static_assert(base_template<some>::value,"F*");<source>:28:15: error: static assertion failed: F*
How the SFINAE works here? though the std::is_integral<some>::value is going to result in false, it doesn't mean(it does?) that the when<false> is ill-formed and should dispatch the instantiation to the primary class template. Am I missing anything here?
It's the same general idea. You can use enable_if_t or decltype in basically the same way. Now, you're probably used to seeing SFINAE partial specializations like this:
template<class T, class U = void>
struct Foo {};
template<class T>
struct Foo<T, decltype(T::bar())> {};
... Foo<X> ...
Here, Foo<X> is first expanded by the compiler to Foo<X, void> (because you didn't provide U at the "call site", so the default U = void is filled in instead). Then, the compiler looks for the best-matching specialization of class template Foo. If decltype(X::bar()) is in fact void, then Foo<T, decltype(T::bar())> [with T=X] will be a perfect match for Foo<X, void>. Otherwise, the generic Foo<T, U> [with T=X, U=void] will be used instead.
Now for the Hana when example.
template<bool> struct when {};
template<class T, class U = when<true>>
struct Foo {};
template<class T>
struct Foo<T, when<T::baz>> {};
... Foo<X> ...
Here, Foo<X> is first expanded by the compiler to Foo<X, when<true>> (because you didn't provide U at the "call site", so the default U = when<true> is filled in instead). Then, the compiler looks for the best-matching specialization of class template Foo. If when<X::baz> is in fact when<true>, then Foo<T, when<T::baz>> [with T=X] will be a perfect match for Foo<X, when<true>>. Otherwise, the generic Foo<T, U> [with T=X, U=when<true>] will be used instead.
You can replace the simple expression T::baz in my example with any arbitrarily complicated boolean expression, as long as it's constexpr-evaluable. In your original example, the expression was std::is_integral<T>::value.
My CppCon 2017 session "A Soupçon of SFINAE" walks through some similar examples.

template class specialization enable_if and default values

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.