std::enable_if_t doesn't match partial specialization - c++

If my understanding is correct, alias templates are actually templates and hence can be passed as arguments where a class template is expected. Which is what makes the following code valid -
template <template <bool, typename> T>
struct foo {};
foo<std::enable_if_t> x;
It is also possible to use partial specialization to match template classes. Which is why the following code prints "Version 2".
template <typename T>
struct foo {
static void print() {std::cout << "Version 1" << std::endl;}
};
template <template <typename> typename T, typename X>
struct foo<T<X>> {
static void print() {std::cout << "Version 2" << std::endl;}
};
template <typename T>
struct bar {
};
...
foo<bar<int>>::print();
Now my question is, why doesn't the following code print "Version 2", but instead prints "Version 1"
template <typename T>
struct foo {
static void print() {std::cout << "Version 1" << std::endl;}
};
template <template <bool, typename> typename T, bool X, typename Y>
struct foo<T<X, Y>> {
static void print() {std::cout << "Version 2" << std::endl;}
};
foo<std::enable_if_t<true, int>>::print();
Shouldn't the second partial specialization be a better match where T=std::enable_if_t, X=true, Y=int?

As expected, the following prints "Version 2"
foo<std::enable_if<true, int>>::print();
In your example however, the type std::enable_if_t<true, int> is equivalent to int and cannot match cannot match T<X, Y>.
cppreference explains it like this:
An alias template is a template which, when specialized, is equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id
The alias std::enable_if_t can match template <bool, typename> typename, but once specialized it cannot be deconstructed.

Whenever an alias template has arguments, it is immediately replaced with the type it aliases. So, for example, std::enable_if_t<true, int> is immediately replaced by int. This applies even in a dependent context; so for example if you have a function like this:
template <bool b, typename T>
void foo(std::enable_if_t<b, T>);
the compiler rewrites it to:
template <bool b, typename T>
void foo(typename std::enable_if<b, T>::type);
The original alias has disappeared from the equation.
It follows that a type like T<X, Y> can't be used to deduce T as an alias template because whatever type U is provided for T<X, Y> to match against, if it originally had the form A<X, Y> for some alias template A, it would have been replaced by whatever that alias template aliases to, and A would no longer be present. (However, an alias template can be deduced as an argument of a class template that has a template template parameter, since an alias template can be a valid template template argument)

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.

Short way to constrain template parameter without boiler plate code for a struct

consider this example:
#include <iostream>
template <typename T, std::size_t I>
struct Foo
{
};
template <typename T>
struct specializes_foo : std::false_type {};
template <typename T, std::size_t I>
struct specializes_foo<Foo<T, I>> : std::true_type {};
template <typename T>
concept foo = specializes_foo<T>::value;
int main()
{
std::cout << foo<int> << "\n";
std::cout << foo<Foo<int,3>> << "\n";
}
is there a shorter way in C++20? I know that you could do a concept for "specializes template", e.g.:
// checks if a type T specializes a given template class template_class
// note: This only works with typename template parameters.
// e.g.: specializes<std::array<int, 3>, std::array> will yield a compilation error.
// workaround: add a specialization for std::array.
namespace specializes_impl
{
template <typename T, template <typename...> typename>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> typename template_class>
struct is_specialization_of<template_class<Args...>, template_class> : std::true_type {};
}
template <typename T, template <typename...> typename template_class>
inline constexpr bool is_specialization_of_v = specializes_impl::is_specialization_of<T,template_class>::value;
template <typename T, template <typename...> typename template_class>
using is_specialization_of_t = specializes_impl::is_specialization_of<T,template_class>::type;
template<typename T, template <typename...> typename template_class>
concept specializes = is_specialization_of_v<T, template_class>;
However, this fails with non-type template parameters such as "size_t". Is there a way so I could, for example just write:
void test(Foo auto xy);
I know that since C++20 there came a couple of other ways to constrain template parameters of a function, however, is there a short way to say "I dont care how it specializes the struct as long as it does"?
Nope.
There's no way to write a generic is_specialization_of that works for both templates with all type parameters and stuff like std::array (i.e. your Foo), because there's no way to write a template that takes a parameter of any kind.
There's a proposal for a language feature that would allow that (P1985), but it's still just a proposal and it won't be in C++23. But it directly allows for a solution for this. Likewise, the reflection proposal (P1240) would allow for a way to do this (except that you'd have to spell the concept Specializes<^Foo> instead of Specializes<Foo>).
Until one of these things happens, you're either writing a bespoke concept for your template, or rewriting your template to only take type parameters.

How to match only variadic templates with a template template parameter?

Consider the following code:
#include <iostream>
template <template<class...> class C>
struct foo {
foo() { std::cout << "base case\n";}
};
template <template<class> class C>
struct foo< C > {
foo() { std::cout << "single param case\n";}
};
template <template<class,class> class C>
struct foo< C > {
foo() { std::cout << "two param case\n";}
};
template <typename T> struct bar1 {};
template <typename T,typename U> struct bar2 {};
template <typename T,typename U,typename V> struct bar3 {};
template <typename...T> struct barN {};
int main() {
foo<bar1> f;
foo<bar2> g;
foo<bar3> h;
foo<barN> n;
}
Output is (gcc10.2#godbolt):
single param case
two param case
base case
base case
Suppose barX is given and that I have other templates with varying number of type parameters. Some variadic some not.
Is it possible to write a specialization that only matches the variadic template (barN in the above example)?
Very interesting question. Unfortunately the answer is No.
There is no general way to determine if a template had a template parameter pack or just a bunch of regular template parameters with or without defaults.
The reason is that non-variadic templates can bind to variadic template template parameters and the concrete types of a template can bind to a template parameter pack.
So effectively the information is not available via deduction/specialization. And in general this is good - without this feature variadic templates would lose much of their power.
But if we could limit the maximum length of template arguments we could write a trait with a bunch of template specializations. This works because of partial ordering (as you have shown in your question): godbolt
We can determine whether a class template that can be instantiated with 0 template arguments is genuinely variadic or (merely) has defaults for all its non-variadic template arguments, by counting the arguments to an 0-argument instantiation:
template<class> constexpr unsigned argc_v;
template<template<class...> class C, class... A> constexpr unsigned argc_v<C<A...>> = sizeof...(A);
template<template<class...> class, class = void> constexpr bool is_variadic_v = false;
template<template<class...> class C> constexpr bool is_variadic_v<C, std::void_t<C<>>> = argc_v<C<>> == 0;
Then we can use this to build a set of specializations that respectively accept only variadic, 1-argument (with possible default) and 2-argument (with possible default/s) class templates:
template<template<class...> class, class = std::true_type>
struct foo;
template<template<class...> class C>
struct foo<C, std::bool_constant<is_variadic_v<C>>> {
foo() { std::cout << "variable case\n"; }
};
template<template<class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void>> == 1>> {
foo() { std::cout << "single param case\n";}
};
template<template<class, class> class C>
struct foo<C, std::bool_constant<!is_variadic_v<C> && argc_v<C<void, void>> == 2>> {
foo() { std::cout << "two param case\n";}
};
I'm a bit disappointed that the latter argc_v tests are necessary (in C++20 mode); I think it's something to do with P0522 / CWG150.
Demo.

Is moneypunct Object International?

Say I have a templatized function that takes a moneypunct:
template <typename T>
void foo(const T& bar);
I can use typename T:char_type to determine the first moneypunct template argument (whether I'm dealing with a moneypunct<char> or a moneypunct<wchar_t>.) But how can I determine whether the second template argument is true or false (moneypunct<char, true> or moneypunct<char, false>?)
Is the only way to accomplish this to redesign my function to:
template <typename CharT, typename International = false>
void foo(const moneypunct<CharT, International>& bar);
If you only want to take a moneypunct, this would definitely be the best, clearest solution:
template <typename CharT, typename International = false>
void foo(const moneypunct<CharT, International>& bar);
However, you can still determine both template arguments from the original with a type trait:
template <typename> struct isInternational;
template <typename CharT, bool International>
struct isInternational<std::moneypunct<CharT, International>>
: std::integral_constant<bool, International>
{ }
Which you can use:
template <typename T>
void foo(const T& bar) {
// this won't compile if T is not a std::moneypunct
std::cout << isInternational<T>::value << std::endl;
}

template specialisation for a whole set of parameters

Possibly easy to solve, but its hard to find a solution to this:
Is it possible to (partially) specialize for a whole set of types?
In the example "Foo" should be partially specialized for (T,int) and (T,double) with only one template definition.
What I can do is define a specialisation for (T,int). See below. But, it should be for (T,int) and (T,double) with only one function definition (no code doubling).
template <typename T,typename T2>
struct Foo
{
static inline void apply(T a, T2 b)
{
cout << "we are in the generic template definition" << endl;
}
};
// partial (T,*)
template <typename T>
struct Foo<T, int > // here something needed like T2=(int, double)
{
static inline void apply(T a, T2 b)
{
cout << "we are in the partial specialisation for (T,int)" << endl;
}
};
Any ideas how to partially specialize this for (T,int) and (T,double) with one template definition?
If I understood your question correctly, then you can write a base class template and derive from it, as illustrated below:
template <typename T, typename U>
struct Foo_Base
{
static inline void apply(T a)
{
cout << "we are in the partial specialisation Foo_Base(T)" << endl;
}
};
template <typename T>
struct Foo<T, int> : Foo_Base<T, int> {};
template <typename T>
struct Foo<T, double> : Foo_Base<T, double> {};
Although its not one template definition (as you asked for), but you can avoid the code duplication.
Demo : http://www.ideone.com/s4anA
I believe you could do this using Boost's enable_if to enable the partial specialisation for just the types you want. Section 3.1 shows how, and gives this example:
template <class T, class Enable = void>
class A { ... };
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };