I saw the following example of enable_if for C++11:
struct is_64_bit
{
static const bool value = sizeof(void*) == 8;
};
enable_if<is_64_bit::value, void>::type
my_memcpy(void* target, const void* source, size_t n)
{
cout << "64 bit memcpy" << endl;
}
enable_if<!is_64_bit::value, void>::type
my_memcpy(void* target, const void* source, size_t n)
{
cout << "32 bit memcpy" << endl;
}
As I understand, depending on the system architecture, the "my_memcpy" function will be available either for 32 or 64 bit versions. But I'm getting the following error at compilation :
error: ‘type’ in ‘struct std::enable_if<false, void>’ does not name a type
I'm a bit confused because I thought only the 32 version should be available (I'm using Linux Fedora 32 bits).
Maybe is something wrong with this example? or Am I missing something?
Thanks.
std::enable_if
works through the principle of substitution failure is not an error (SFINAE), which states that when certain types of error occur in instantiating a function template, the program continues to compile with that function template not participating in overload resolution.
For SFINAE to kick in, (a) it has to be used on a function (or method) template and (b) it has to be dependent on a template parameter. Your program fails on both counts.
To make the enable_if
dependent on a template parameter, the easiest thing is to add a default parameter:
template<typename T = void> typename enable_if<is_64_bit::value, T>::type
my_memcpy(void* target, const void* source, size_t n)
However, this is not in general a sensible use of enable_if
; since it depends on intercepting compile errors it tends to be expensive. In your case a template specialization would be a much better idea:
#include <iostream>
template<int = sizeof(void *)> void my_memcpy(void* target, const void* source, size_t n);
template<> void my_memcpy<8>(void* target, const void* source, size_t n) {
std::cout << "64 bit memcpy" << std::endl;
}
template<> void my_memcpy<4>(void* target, const void* source, size_t n) {
std::cout << "32 bit memcpy" << std::endl;
}
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;
}
SFINAE is for templates. What you need is to use templates, as the other answers mentioned, or just have a compile-time branch, which IMO is the more appropriate solution (rather than introduce a needless template):
struct is_64_bit :
std::integral_constant<bool, sizeof(void*) == 8>
{};
namespace detail
{
void my_memcpy(void* target, const void* source, std::size_t n, std::true_type)
{
std::cout << "64 bit memcpy" << std::endl;
}
void my_memcpy(void* target, const void* source, std::size_t n, std::false_type)
{
std::cout << "32 bit memcpy" << std::endl;
}
}
void my_memcpy(void* target, const void* source, std::size_t n)
{
my_memcpy(target, source, n, is_64_bit());
}
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