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?
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;
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With