Reputation: 2722
I've implemented this function here (in a single header file).
//header.h
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <stdint.h>
#include <type_traits>
template < typename T,
typename = std::enable_if<std::is_integral<T> >::type >
void f(T t) {
printf("Integer function\n");
}
template void f(int i);
The main is below...
#include "header.h"
int main(int argc, char** argv) {
int p = 3;
f(p);
}
It compiles... no problem, but it doesn't show the content of the printf, what am i missing?... (i'm using eclipse as IDE, it could be a problem?)
One more thing... does it make sense this implementation? (i would like to write different code based on the type in input, whether it is unsigned or not).
template < typename T,
typename = std::enable_if<std::is_integral<T>::value>,
typename = std::enable_if<std::is_unsigned<T>::value>
>
void f(T t) {
printf("Unsigned f version.\n");
}
template < typename T,
typename = std::enable_if<std::is_integral<T>::value>,
typename = std::enable_if<std::is_signed<T>::value>
>
void f(T t) {
printf("Signed f version.\n");
}
In this case it doesn't compile... so how to deal with this?
Thank you
Update...
I tried the modification suggested in the first code, it gives me the following error
..\header.h:19:58: error: type/value mismatch at argument 1 in template parameter list for 'template<bool <anonymous>, class _Tp> struct std::enable_if'
typename = std::enable_if<std::is_integral<T> >::type >
^
..\header.h:19:58: error: expected a constant of type 'bool', got 'std::is_integral<_Tp>'
..\header.h:19:61: error: expected '>' before 'type'
typename = std::enable_if<std::is_integral<T> >::type >
^
..\main.cc: In function 'int main(int, char**)':
Update 2...
Ok guys... I've been trying to figure out what it is i do not understand... so i'm digging into the code of type_traits (so my goal is to understand what i've done wrong...)
I report the code for the enable_if (from type_traits).
template<bool, typename _Tp = void>
struct enable_if //Lukkio decl. 1
{ };
// Partial specialization for true.
template<typename _Tp>
struct enable_if<true, _Tp> //Lukkio decl. 2
{ typedef _Tp type; };
Because i want to understand what happens to a line like:
template<
typename T,
typename = std::enable_if<std::is_integral<T>::value>
>
void function(T t) {
printf("Is integral!\n");
}
So assuming T
is fixed (let's say int, which is integrale) the std::enable_if::value>
use the decl. 1, assuming however the _Tp
is of type void
so actually what happens, in terms of reduction (i now substitute to T
the keyword int
... should be something like
template<
typename int,
typename = std::enable_if<std::is_integral<int>::value>
>
namely
template<
typename int,
typename = std::enable_if<1>
>
So my question is... what does the second typename stands for?
Upvotes: 1
Views: 1030
Reputation: 302817
In this case it doesn't compile... so how to deal with this?
Consider your templates again:
template < typename T,
typename = std::enable_if<std::is_integral<T>::value>,
typename = std::enable_if<std::is_unsigned<T>::value>
>
void f(T t) {
You're not actually using SFINAE. Your unnamed types are just std::enable_if<X>
. That is always a valid type. In order for SFINAE to apply, you actually need to add ::type
at the end or use std::enable_if_t
, that is:
template < typename T,
typename = std::enable_if_t<std::is_integral<T>::value>,
typename = std::enable_if_t<std::is_unsigned<T>::value>
>
void f(T t) {
Now, you'd run into two ambiguous function templates, which you can disambiguate by using the enable_if_t
as a value instead of a type. That is:
template < typename T,
std::enable_if_t<std::is_integral<T>::value &&
std::is_unsigned<T>::value>* = nullptr
>
void f(T t) { ... }
And similarly for the other case.
Upvotes: 2
Reputation: 70516
SFINAE on default function template arguments doesn't really scale. You can only use this if you want to restrict a single overload to a particular class of types.
For non-overlapping multiple overloads (like your signed/unsigned integrals), you can SFINAE on the return type like this:
#include <cstdio>
#include <type_traits>
template < typename T>
auto f(T t)
-> std::enable_if_t<std::is_unsigned<T>::value && std::is_integral<T>::value>
{
printf("Unsigned f version.\n");
}
template < typename T>
auto f(T t)
-> std::enable_if_t<std::is_signed<T>::value && std::is_integral<T>::value>
{
printf("Signed f version.\n");
}
int main()
{
unsigned u = 1;
signed s = 1;
f(u);
f(s);
}
Note: the enable_if_t<T>
is a type alias for typename enable_if<T>::type
that came available in C++14 (you can write it yourself)
Upvotes: 3