Why Compile Error with Enable_If

Why compile error with enable_if

Because you use enable_if without using the template parameter T in your function templates. If you want to specialize for when the struct S has a certain template parameter value N, you'll need to use class template specialization.

template <int N, class Enable = void> 
struct S { };

template <int N>
struct S<N, typename std::enable_if<N == 1>::type>
{
....
};

Are these the expected errors with std::enable_if, or am I using it incorrectly?

std::enable_if_t is a template that causes substitution to fail if the condition is false. Because of SFINAE this does not cause the program to be ill-formed, when it happens during overload resolution. You are passing it a false value as the default value, so for a call to function(), without any additionally specified template parameters, overload resolution will fail.

If you change template <int Z = 0> part to int Z = 1 then I would expect the code to compile.


More to the second part of the question: Are these other errors expected?

smelly_template.cpp:3:40: note: candidate: ‘template<int Z> std::enable_if_t<(Z != 0)> function()’
3 | template <int Z=0> std::enable_if_t<Z> function () { }
| ^~~~~~~~
smelly_template.cpp:3:40: note: template argument deduction/substitution failed:
In file included from smelly_template.cpp:1:

Yes, the compiler tries to help you by showing what it tried, whenever overload resolution fails. Modern versions of gcc and clang will show you every overload that was available and why it couldn't be used. In this case, it is explaining why overload resolution failed with the one overload that it tried. These kinds of errors when overload resolutions fails are extremely helpful in large programs.

compile error - templates, enable_if

You declare Citizen to be a class template taking 5 template parameters:

template <typename T, T, T, bool, typename >
class Citizen { ... };

and then try to define the constructor using only 4 template parameters:

template <typename T, T minAge, T maxAge, bool isarmed>
Citizen<T, minAge, maxAge, isarmed>::Citizen(T health, T age)

There is no such previously declared 4-template-parameter Citizen, hence the error. You still need that last template parameter.


Note that SFINAE here doesn't make much sense unless you have some other non-arithmetic Citizen class template (which itself doesn't make much sense). Just have a static_assert for T being an arithmetic type.

Why does failed enable_if lead to compile time error?

As I understand it should lead to SFINAE

SFINAE means "substitution failure is not an error". You need substitution to occur in order to SFINAE out something. In this case, it is sufficient to add a default template parameter to your copy constructor:

template <typename D = Data>
Container(const typename std::enable_if<std::is_copy_constructible<D>::value,
self_type>::type& other_data) :
m_data(other_data.m_data)
{
}

live example on wandbox

std::enable_if is generating error when used on functions of same name inside a template class

Like @Evg mentioned you have to need the member function to be templated as well.
In addition, you need to provide default values for them, like usual nullptr defaulting.

See a demo

#include <iostream>
#include <type_traits>

template<typename T>
class Coverage {
public:
Coverage(T type) :_type(type) {}

//signed char
template<typename Type = T> // templated the member!
int getNb(typename std::enable_if<std::is_signed<Type>::value, void>::type* = nullptr)
{
return 1;
}

//unsigned int
template<typename Type = T> // templated the member!
int getNb(typename std::enable_if<std::is_unsigned<Type>::value, void>::type* = nullptr)
{
return 2;
}
};

Or provide a trailing return SFINAE for each functions

See a demo

#include <iostream>
#include <type_traits>

template<typename T>
class Coverage {
public:
Coverage(T type) :_type(type) {}

template<typename Type = T>
auto getNb() -> typename std::enable_if<std::is_signed<Type>::value, int>::type
{
return 1;
}

template<typename Type = T>
auto getNb() -> typename std::enable_if<std::is_unsigned<Type>::value, int>::type
{
return 2;
}

private:
T _type;
};

Errors with std::enable_if

When you use std::enable_if<false, bool>::type literally there is nothing to be substituted. Thus, the code is always wrong and the compilers reports the error as such. SFINAE only applies if there is "S" (substitution) which ended up to "F" (failure).

c++11 enable_if error

The template template< bool B, class T = void > struct enable_if is specialized so that it only has a typedef type when the condition B is true.

Your compiler is correct. There is no typedef for type in struct std::enable_if<false, void>. There is only a typedef in struct std::enable_if<true, void>.

For more info look here.

So to fix your problem you need to make sure that the enable_if which has a B that evaluates to false never gets compiled. You can achieve this with the help of SFINAE by making my_memcpy a function template. The compiler will then not report an error when failing to compile the function template where B evaluates to false and will successfully compile and use the function where B evaluates to true.

#include <iostream>
#include <type_traits>

using namespace std;

struct is_64_bit
{
static const bool value = sizeof(void*) == 8;
};

template<typename T>
typename enable_if<is_64_bit::value, T>::type
my_memcpy(T* target, const T* source, size_t n)
{
cout << "64 bit memcpy" << endl;
}

template<typename T>
typename enable_if<!is_64_bit::value, T>::type
my_memcpy(T* target, const T* source, size_t n)
{
cout << "32 bit memcpy" << endl;
}

std::enable_if not compiling (invalid template argument)

The best way and the less intrusive one to do SFINAE is to use a non type template parameter:

template <class Archive, typename T,
std::enable_if_t<std::is_base_of<Serializable, T>::value, int> = 0
>
Archive &operator>>( Archive &in, std::unique_ptr<T>& ptr)
{
// ...
}

And if you want to make it shorter:

template<typename Cond>
using my_enable = std::enable_if_t<Cond::value, int>;

template<typename T>
using is_serializable = std::is_base_of<Serializable, T>;

template <class Archive, typename T, my_enable<is_serializable<T>> = 0>
Archive &operator>>( Archive &in, std::unique_ptr<T>& ptr)
{
// ...
}

It should be way less intrusive and cause less problem with type deduction.



Related Topics



Leave a reply



Submit