SFINAE template specialization not working - c++

#include <iostream>
#include <utility>
using namespace std;
template <size_t N, typename V = int> // works if typename V = void
struct T
{
const static int size = 0;
};
template <size_t N>
struct T<N,typename std::enable_if<(N>10)>::type>
{
const static int size = 1;
};
int main (){
cout << T<9>::size << endl; // 0
cout << T<19>::size << endl;// 0 WHY?
cout << T<10>::size << endl; //0
}
Not sure why the output is the way it is, why isn't the specialization getting picked up?

Right now, the second argument is always int, since it's the default value:
int main (){
cout << T<9, int>::size << endl;
cout << T<19, int>::size << endl;
cout << T<10, int>::size << endl;
}
But the expression typename std::enable_if<(N>10)>::type won't ever yield an int, so your specialization won't be picked. The default type for std::enable_if::type is void.
It will work only if you're sending void in the second argument (of course you don't want that):
int main (){
cout << T<9, void>::size << endl;
cout << T<19, void>::size << endl;
cout << T<10, void>::size << endl;
}
To make it work like you want, you must either make the default argument to be void, or make your constraint to always yield an int type.
template <size_t N, typename V = void>
struct T
{
const static int size = 0;
};
Or alternatively make your constrain yeild the type of your default argument:
typename std::enable_if<(N>10), int>::type
In fact, you could put any type and it wouldn't change a thing. As long as the two type matches:
// silly but works, the two types are the same.
template<size_t N, typename = decltype(std::cout)>
struct T { /* ... */ };
template <size_t N>
struct T<N, typename std::enable_if<(N>10), decltype(std::cout)>::type> { /* ... */ };

Due to the template parameter with default argument in your primary template, when you write T<19>::size, what you're really writing is T<19, int>::size.
Now, there exist partial specializations for your template, thus, when figuring out which specialization a simple-template-id like T<19> refers to, the compiler will try to find a matching partial specialization. To do so, it will look for a match between the actual template arguments 19, int and the template arguments given in your partial specialization
template <size_t N>
struct T<N, typename std::enable_if<(N>10)>::type>
{ // ^-------------- this here -------------^
…
};
The first step is to figure out the arguments for the parameters of your partial specialization itself. In your case, the partial specialization has one parameter template <size_t N>. This parameter is deduced by comparing the parameterized type T<N, typename std::enable_if<(N>10)>::type> to the actual type T<19, int>. Deduction yields 19 as argument for parameter N. Substituting back into the specialization, we get
struct T<N, void>
as the deduced argument list for our specialization because std::enable_if<true>::type is void. The argument list 19, void is not the same as 19, int, therefore, the specialization is not a match and the primary template will be used.
So to make this work, you have to make sure that the type produced by your std::enable_if argument matches the default argument of the last parameter of the primary template, i.e., either change the default argument to be void
template <size_t N, typename = void>
struct T
{
…
};
or make your std::enable_if produce int in the cases where you want the specialization to be used
template <size_t N>
struct T<N, std::enable_if_t<(N>10), int>>
{
…
};

Related

Constant template arguments for functions

I have the defined the following types:
template <typename TType>
struct symbol{};
template <typename TAtom, unsigned IDim>
struct tensor_type {};
template <unsigned IDim>
using real = tensor_type<double, IDim>;
template <unsigned IDim>
using index = tensor_type<int, IDim>;
I now want to call functions with instances of the type symbol like this:
template <template<typename TType, unsigned IDim> typename TTemplate, typename TType, unsigned IDim>
void f(symbol<TTemplate<TType, IDim>>* sym) {
std::cout << "higher dimension" << std::endl;
}
int main() {
symbol<real<0>>* p1 = new symbol<real<0>>();
symbol<index<0>>* p2 = new symbol<index<0>>();
symbol<real<1>>* p3 = new symbol<real<1>>();
symbol<index<5>>* p4 = new symbol<index<5>>();
f(p1); //dimension 0
f(p2); //dimension 0
f(p3); //higher dimension
f(p4); //higher dimension
}
This works fine and well. But now I want to differ between symbols of the dimension 0 and all the other dimensions, but I do not know how to properly declare an overloaded function properly with the templates to achive this.
I have tried the following:
template <typename TType>
void f(symbol<TType<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: ‘TType’ is not a template during compilation and
template <template<typename TType> typename TTemplate, typename TType>
void f(symbol<TTemplate<TType>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: wrong number of template arguments (2, should be 1).
I cannot include the code for both dimensions in one function because in the actual implementation types with dimension 0 have different attributes than the one with higher dimensions, so even if I use a case distinction in the function the compiler complains that some passed arguments do not have the needed attribute.
One possiblity is to declare a function for every possible type for dimension 0 like this
void f(symbol<real<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
but since I have multiple types in the actual implementation and all the functions would look exactly the same (besides the signature) I wanted to avoid redundant code.
Is there some way to achive this more elegant?
In your shown example, there is only one relevant class template: tensor_type
The others declared with using are just aliases for this one. They do not define new class templates or types.
So with that it is enough to simply specify that template in the overload:
template <typename T>
void f(parameter_symbol<tensor_type<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
If you have multiple templates, some of which you haven't shown, then
template <template<typename, unsigned> typename TTemplate, typename T>
void f(parameter_symbol<TTemplate<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
will work. Note that the kinds of template parameter in the template template parameter template<typename, unsigned> typename TTemplate must match the class template that you are going to use as argument. (As one exception, you can also replace unsigned with auto.)
If you have multiple templates with different kinds of template parameters, then you need define such overloads for all cases.
If the dimension is not always in the same position in the parameter list, then this approach doesn't work.
It would be simpler to have the relevant classes provide a constexpr static member variable idim which indicates the dimension, or a constexpr static function returning it. Or alternatively a specialized free variable template or function template.
Then you can have a function overload taking any type and constrain it through SFINAE or if you can use C++20, a requires class:
template <typename T>
requires(T::idim == 0)
void f(parameter_symbol<T>* sym) {
std::cout << "dimension 0" << std::endl;
}

Overload of variadic template function with another template

I am trying to figure out how to "overload" a variadic function template with a "more specialized" variadic function template. For example:
#include <iostream>
template <typename... ARGS_>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
struct Test {
};
template <typename... ARGS_>
void foo(void(*fn)(ARGS_..., Test*)) {
std::cout << "Test function pointer foo" << std::endl;
}
void test1(int a, int b) {
std::cout << "test1()" << std::endl;
}
void test2(int a, int b, Test* x) {
std::cout << "test2()" << std::endl;
}
int main() {
foo(&test1);
foo(&test2);
return 0;
}
The output of this code is:
Generic function pointer foo
Generic function pointer foo
Rather than:
Generic function pointer foo
Test function pointer foo
as I want.
Conceptually, I am trying to notate "Use template method A if you have any type(s) of arguments where the LAST on is Test* and use template method B if the last type is NOT Test*."
What is the correct method to accomplish this type of behavior?
SFINAE'd overloads based on the last parameter pack argument
You can add mutually exclusive overloads based on whether the last type in the variadiac parameter pack is Test* or not:
#include <type_traits>
template <typename... Ts>
using last_t = typename decltype((std::type_identity<Ts>{}, ...))::type;
struct Test {};
template <
typename... ARGS_,
std::enable_if_t<!std::is_same_v<last_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
template <
typename... ARGS_,
std::enable_if_t<std::is_same_v<last_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
// Special case for empty pack (as last_t<> is ill-formed)
void foo(void (*fn)()) { std::cout << "Zero args" << std::endl; }
making use if C++20's std::type_identity for the last_t transformation trait.
Used as:
void test1(int, int b) {}
void test2(int, int b, Test *x) {}
void test3(Test *) {}
void test4() {}
int main() {
foo(&test1); // Generic function pointer foo
foo(&test2); // Test function pointer foo
foo(&test3); // Test function pointer foo
foo(&test4); // Zero args
}
Avoiding the zero-arg special case as an overload?
The zero-arg foo overload can be avoided in favour of tweaking the last_t trait into one which also accepts an empty pack, such that a query over the empty pack is used to resolve to the generic overload. Neither its semantics nor its implementation becomes as straight-forward and elegant, however, as "the last type in an empty type list" does not make much sense, meaning the trait need to be tweaked into something different:
template <typename... Ts> struct last_or_unique_dummy_type {
using type = typename decltype((std::type_identity<Ts>{}, ...))::type;
};
template <> class last_or_unique_dummy_type<> {
struct dummy {};
public:
using type = dummy;
};
template <typename... Ts>
using last_or_unique_dummy_type_t =
typename last_or_unique_dummy_type<Ts...>::type;
template <typename... ARGS_,
std::enable_if_t<!std::is_same_v<
last_or_unique_dummy_type_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
template <typename... ARGS_,
std::enable_if_t<std::is_same_v<last_or_unique_dummy_type_t<ARGS_...>,
Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
Using an additional overload for the empty pack is likely the least surprising approach.
C++20 and the identity_t trick
In case you are not yet at C++20, an identity meta-function is trivial to write yourself:
template <typename T>
struct type_identity {
using type = T;
};
Any specialization of this class template, unless partially/explicitly specialized otherwise (which is UB for the STL type), is trivial and default-constructible. We leverage this in the definition of last_t above: default-constructing a series of trivial types in an unevaluated context, and leveraging that the last of those types embeds the input to the identity trait whose specialization is that trivial type, and whose wrapped alias declaration type is the type of the last parameter in the variadic parameter pack.
Check that the last element of ARGS... is Test*.
It won't do that for you this way. One ways is:
template<class...Ts>
struct last_type {};
template<class T1, class T2, class...Ts>
struct last_type<T1, T2, Ts...>:last_type<T2, Ts...>{};
template<class T>
struct last_type<T>{
using type=T;
};
template<class...Ts>
using last_type_t = typename last_type<Ts...>::type;
now you just:
template <typename... ARGS_>
requires std::is_same_v<last_type_t<ARGS_...>, Test*>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
Live example.
Without concepts, you have to replace that requires clause:
template <typename... ARGS_,
std::enable_if_t<std::is_same_v<last_type_t<ARGS_...>, Test*>, bool> = true
>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
which is a more obscure "cargo cult" way to basically say the same thing. (you also need to invert enable if clause in the other overload; not = false but !, and handle 0 arg case (prepend void on the type list?))
The reason why your attempt doesn't work is that C++ makes ... matching insanely greedy. It is generally not a good idea to put things you want pattern matched behind it in a context where pattern matching of parameters is done.

C++ Template Specialization / Partial and Full

I was trying to understand Template Partial and Full Specialization and have this below code:
#include <iostream>
using namespace std;
//Template in C++is a feature. We write code once and use it for any data type including user defined data types.
//What if we want a different code for a particular data type - Template Specialization
template <class T, class Y>
void fun(T a, Y b)
{
cout << "The main template fun(): " << a << " " << b << endl;
}
//If a specialized version is present, compiler first checks with the specialized version and then the main template.
//Compiler first checks with the most specialized version by matching the passed parameter with the data type(s) specified in a specialized version.
template<>
void fun(const char* a, const char* b)
{
cout << "Specialized Template for int type: " << a << " " << b << endl;
}
//Partial specialization allows to specialize only some arguments of a class template, as opposed to explicit full
template<class T>
void fun(T a, int b)
{
cout << "Partial Template specialization for int type: " << a << " " << b << endl;
}
int main()
{
fun<int, int>('a', 10);
fun< const char*, const char*>("Hello", "Morning");
fun<float, float>(10.14, 19.89);
}
Note that in main I am specifying the data types and below is the output:
The main template fun(): 97 10
Specialized Template for int type: Hello Morning
The main template fun(): 10.14 19.89
But when I execute the main code below way:
int main()
{
fun('a', 10);
fun("Hello", "Morning");
fun(10.14, 19.89);
}
This is the output I get:
Partial Template specialization for int type: a 10
Specialized Template for int type: Hello Morning
The main template fun(): 10.14 19.89
So what does the actual C++ Template Partial / Full specialization states - do we need to specify the data types in template argument to invoke - also in many websites I have seen following signature for Partial specialization:
template<class Y, const char*>
void fun(Y a, const char* b)
Rather than
template<class Y>
void fun(Y a, const char* b)
Similarly for full specialization - what is the exact way to write and call Partial / Full template specialized function / class?
The comments cover the general principles, but there are specific questions here worth answering. First, a lemma: the template parameters of a function template need not be related to its parameter types:
template<class T>
T make_1() {return T(1);}
auto v=make_1<std::vector<std::string>>(); // {""}
template<class T,class U>
void f(std::conditional_t<sizeof(T)<sizeof(U),int,long> i);
This is why your “partial specialization” is not a candidate for fun<int, int>(…). The template arguments aren’t just instructions for how to build the parameter list one by one, but rather must match the actual template parameters for the function template in question. There’s only one of those, so they don’t match. True partial specializations still use the primary template’s template parameter list; they just match against certain patterns in the list of arguments for it.
Meanwhile,
template<class Y, const char*>
void fun(Y a, const char* b)
is valid but doesn’t mean what you think for the same reason: const char* in the template parameter list introduces an unnamed (non-type) template parameter of type const char*, so that you’d have to use it like
char global;
void g() {fun<char,&global>('a',"foo");}
(with no deduction for Y!), only to have that &global ignored entirely,

Is it possible to write class template that works for all integral types?

as you know template parameters may be types or integral values,
so for example you can have
template<typename T, size_t BufferSize>
small_vector {
//...
Problem I have is that I want to write template that would work on different integral types.
So something like this:
constexpr short sa=1, sb=2, sc=3;
my_template_class1<sa,sb,sc>;
constexpr int ia=1, ib=2, ic=3;
my_template_class2<ia,ib,ic>;
In other words I know template type parameter can be int, what I want is to have a template that will work with integer template parameters for all integer types.
Note that I can not just do all the computations in int64 since it would not work for uint64.
I think you're interested in a template which will only work for integral types. What you're looking for is std::enable_if. By using this, a template will throw a compile error if you try to use anything but an integral type to create it. It will also work for any and all integral types.
#include <iostream>
#include <type_traits>
// 1. the return type (bool) is only valid if T is an integral type:
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;
}
Usually you achieve this by adding an extra "enabling" template parameter:
// the partial specialization of A is enabled via a template parameter
template<class T, typename BufferSize, class Enable = void>
class small_vector {}; // primary template, not used
template<class T, typename BufferSize>
class small_vector<class T, typename BufferSize, typename std::enable_if<std::is_integral<BufferSize>::value>::type> {
}; // specialization for various int types
Having said that, I would suggest not to put much efforts in various BufferSize implementations. They are likely to be used in compile-time only consume no memory or CPU.

enable_if cannot be used to disable this declaration

I evidently have not enough experience with SFINAE to handle this problem. I actually have the impression that it worked until now, and this kind of problem started to appear like in the last half an hour, everywhere in my code.
#include <iostream>
using namespace std;
template <unsigned int N, typename = typename enable_if <N >= 100> :: type>
struct more_than_99
{
};
int main()
{
more_than_99 <0> c;
}
It says
No type named 'type' in 'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
on the line corresponding to the template declaration. What is going on? I have always used this kind of syntax to enable and disable my template classes and it has always thrown errors on the line of instantiation, rather than on the line of the declaration..
Could you please pedantically explain what am I doing wrong here?
The other answers are correct about why the error happens at the template definition rather than the instantiation.
I need an error to be thrown when trying to instantiate something like `more_than_99 <0> x;' on the line where I try to instantiate it. Something like "hey, this type doesn't exist".
How about something like this?
template <unsigned int N, bool B = (N>=100)>
struct more_than_99;
template <unsigned int N>
struct more_than_99<N,true>
{};
int main()
{
more_than_99 <0> c; // error: implicit instantiation of undefined template 'more_than_99<0, false>'
}
To make it a bit more robust, and to attempt to prevent accidentally instantiating more_than_99<0,true>, this also works (C++11):
template <unsigned int N, bool B>
struct _impl_more_than_99;
template <unsigned int N>
struct _impl_more_than_99<N,true>
{};
template <unsigned int N>
using more_than_99 = _impl_more_than_99<N, (N>=100)>;
int main()
{
more_than_99 <0> c; // error: implicit instantiation of undefined template '_impl_more_than_99<0, false>'
}
Although the error message references the _impl_ type.
You could hide the _impl_ in a detail namespace or something, and just document the more_than_99 alias as if it were the actual type.
However, you will not be able to prevent malicious instantiation of _impl_more_than_99<0,true>.
N is not a dependent non-type template parameter; [temp.dep.temp]/p2
A non-type template-argument is dependent if its type is dependent or the constant expression it specifies is value-dependent.
Therefore instead of a substitution failure occurring, the error is emitted directly from the ill-formed code.
enable_if makes sense if you have a class specialization (or a function overload). It is used to choose between one implementation and another depending on a template parameter, not to trigger an error if the condition is not met.
The idea is "enable this specialization if the condition is met, otherwise fall back to the non-specialized version".
In your case, you probably want something like this:
#include <iostream>
#include <type_traits>
using namespace std;
template<unsigned int N, typename = void >
struct more_than_99
{
// Implementation if N <= 99
enum { value = false };
};
template <unsigned int N>
struct more_than_99<N, typename enable_if <N >= 100> :: type>
{
// Implementation if N >= 100
enum { value = true };
};
int main()
{
cout << more_than_99 <0>::value << endl; //false
cout << more_than_99 <100>::value << endl; //true
}
From http://en.cppreference.com/w/cpp/types/enable_if: (emphasis mine)
This metafunction is a convenient way to leverage SFINAE to conditionally remove functions from overload resolution based on type traits and to provide separate function overloads and specializations for different type traits. std::enable_if can be used as an additional function argument (not applicable to operator overloads), as a return type (not applicable to constructors and destructors), or as a class template or function template parameter.
You cannot use it to enable or disable a class or a struct.
Perhaps you are looking for something like:
namespace detail
{
struct more_than_99 {};
template <bool> Helper;
template <> Helper<true>
{
using type = more_than_99;
};
}
template <unsigned int N> struct selector
{
using type = typename detail::Helper<N >= 100>::type
};
using type = selector<10>::type; // Error.
using type = selector<100>::type; // OK.
// type == detail::more_than_99
Use static assert:
template<unsigned int N>
struct more_than_99
{
static_assert(N >= 100, "N must be more than 99");
};
more_than_99<1> m1;
Results in compilation error something along the lines of:
testM99.cpp:6:3: error: static_assert failed "N must be more than 99"
static_assert(N >= 100, "N must be more than 99");
^ ~~~~~~~~
testM99.cpp:12:19: note: in instantiation of template class 'more_than_99<1>'
requested here
more_than_99<1> m1;