Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++11 enable_if error

Tags:

c++

c++11

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.

like image 483
user1274605 Avatar asked Sep 27 '12 15:09

user1274605


3 Answers

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;
}
like image 125
ecatmur Avatar answered Nov 20 '22 08:11

ecatmur


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;
}
like image 37
cyon Avatar answered Nov 20 '22 08:11

cyon


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());
}
like image 5
GManNickG Avatar answered Nov 20 '22 10:11

GManNickG