2012-09-27 12 views
9

vi el siguiente ejemplo de enable_if para C++ 11:C++ 11 error enable_if

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; 
} 

Según entiendo, dependiendo de la arquitectura del sistema, la función "my_memcpy" estará disponible ya sea para 32 o versiones de 64 bits. Pero me estoy haciendo el siguiente error durante la compilación:

error: ‘type’ in ‘struct std::enable_if<false, void>’ does not name a type 

Estoy un poco confundido porque pensaba que sólo la versión 32 debe estar disponible (estoy usando Linux Fedora 32 bits).

Tal vez hay algún problema con este ejemplo? ¿O me estoy perdiendo algo?

Gracias.

Respuesta

9

La plantilla template< bool B, class T = void > struct enable_if está especializada por lo que solo tiene un typedef type cuando la condición B es true.

Su compilador es correcto. No hay typedef para type en struct std::enable_if<false, void>. Solo hay un typedef en struct std::enable_if<true, void>.

Para más información mira here.

Para solucionar su problema, necesita asegurarse de que el enable_if que tiene una B que evalúa a false nunca se compile. Puede lograr esto con la ayuda de SFINAE haciendo my_memcpy una plantilla de función. El compilador no informará un error al no poder compilar la plantilla de función donde B evalúa a false y compilará y utilizará correctamente la función donde B evalúa como 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; 
} 
+0

Gracias cyon. Está resuelto ahora. También entiendo mejor este tema. :) – user1274605

10

std::enable_if obras a través del principio de substitution failure is not an error (SFINAE), que establece que cuando determinados tipos de errores se producen en una instancia de una plantilla de función, el programa continúa compilando con la plantilla de función que no participan en la resolución de sobrecarga.

Para que SFINAE se active, (a) tiene que ser utilizado en una plantilla de función (o método) y (b) tiene que depender de un parámetro de plantilla. Su programa falla en ambos casos.

Para hacer que el dependiente enable_if en un parámetro de plantilla, lo más fácil es añadir un parámetro por defecto:

template<typename T = void> typename enable_if<is_64_bit::value, T>::type 
my_memcpy(void* target, const void* source, size_t n) 

Sin embargo, esto no es, en general, un uso sensato de enable_if; ya que depende de la interceptación de errores de compilación, tiende a ser costoso. En su caso, una especialización de plantilla sería una idea mucho mejor:

#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; 
} 
+0

+1 para "Para que SFINAE ingrese, (a) tiene que ser utilizado en una plantilla de función (o método) y (b) tiene que depender de un parámetro de plantilla." Ahora realmente lo entiendo – Claudiu

3

SFINAE es para plantillas. Lo que necesita es utilizar plantillas, como las otras respuestas mencionadas, o si sólo tiene una sucursal en tiempo de compilación, que la OMI es la solución más apropiada (en lugar de introducir una plantilla innecesaria):

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()); 
}