Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 699
Matteo Monti Avatar asked May 21 '15 17:05

Matteo Monti


4 Answers

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;
like image 173
Sean Avatar answered Sep 18 '22 12:09

Sean


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>.

like image 4
Apples Avatar answered Nov 17 '22 23:11

Apples


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.

like image 4
David G Avatar answered Nov 17 '22 23:11

David G


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
}
like image 4
sbabbi Avatar answered Nov 18 '22 01:11

sbabbi