2010-11-11 9 views
11

En código:Cómo utilizar enable_if para permitir funciones miembro sobre la base de parámetro de plantilla de clase

template<class T> 
struct is_builtin 
{ 
    enum {value = 0}; 
}; 

template<> 
struct is_builtin<char> 
{ 
    enum {value = 1}; 
}; 

template<> 
struct is_builtin<int> 
{ 
    enum {value = 1}; 
}; 

template<> 
struct is_builtin<double> 
{ 
    enum {value = 1}; 
}; 

template<class T> 
struct My 
{ 
    typename enable_if<is_builtin<T>::value,void>::type f(T arg) 
    { 
     std::cout << "Built-in as a param.\n"; 
    } 


    typename enable_if<!is_builtin<T>::value,void>::type f(T arg) 
    { 
     std::cout << "Non - built-in as a param.\n"; 
    } 
}; 

struct A 
{ 
}; 

int main() 
{ 
    A a; 
    My<int> m; 
    My<A> ma; 
    m.f(1); 
    ma.f(a); 
    return 0; 
} 

Recibo un error:

error C2039: 'type' : is not a member of 'std::tr1::enable_if<_Test,_Type>'  

Obviamente, yo no entiendo cómo utilizar enable_if. Lo que estaba pensando era que puedo habilitar una o la segunda función miembro de un conjunto de funciones miembro durante el tiempo de compilación, pero no funciona. ¿Alguien podría explicarme cómo hacerlo correctamente?
Editado
Lo que realmente no puedo entender es por qué no hay typedef en uno de esos def. El compilador no puede encontrarlo y no lo compilará.

Respuesta

13

No puede usar los parámetros de plantilla de clase para obtener SFINAE para las funciones de miembro.

o hay que hacer

  • hacen la función de miembro de una plantilla de función miembro de su lugar y utilizar enable_if en los parámetros de plantilla de la plantilla función miembro o

  • movimiento la función miembro f en una clase política y especialice la plantilla de clase usando enable_if.

+0

Podría dar algún ejemplo por favor? –

+0

@There: hay un ejemplo del uso del segundo enfoque (que especializa una plantilla de clase) en [la documentación Boost 'enable_if'] (http://beta.boost.org/doc/libs/1_44_0/libs/utility/enable_if .html) (ver sección 3.1). –

+0

gracias por el enlace. Voy a leerlo ahora. –

-2

enable_if espera una metafunción. Para usar un bool necesita enable_if_c. Me sorprende que no estés recibiendo errores explicando ESE problema.

Puede corregir su metafunción declarando un typedef 'type' dentro que es simplemente él mismo. A continuación, puede utilizar boost::enable_if<is_builtin<T>>::type

+1

Utilizo std :: enable_if –

0

Así es como funciona (nótese que por conveniencia Substituí su is_builtin rasgo con std::is_arithmetic y se utiliza más de C++ 11 cosas, pero funciona de ninguna manera):

template<class T> 
struct My 
{ 
    template<typename T_ = T, std::enable_if_t<std::is_arithmetic<T_>::value>* = nullptr> 
    void f(T_ arg) 
    { 
     std::cout << "Built-in as a param.\n"; 
    } 

    template<typename T_ = T, std::enable_if_t<!std::is_arithmetic<T_>::value>* = nullptr> 
    void f(T_ arg) 
    { 
     std::cout << "Non - built-in as a param.\n"; 
    } 
}; 

DEMO

La parte crucial es poner el parámetro de plantilla en el contexto inmediato utilizando un parámetro de plantilla de función por defecto T_ que es igual al parámetro de plantilla de clase T. Para más detalles, vea this question.

1

Usted puede fijar su código utilizando modificados enable_if

template < typename T > 
struct __Conflict {}; 

template <bool B, class T = void> 
struct __enable_if { typedef __Conflict<T> type; }; 

template <class T> 
struct __enable_if<true, T> { typedef T type; }; 

Ejemplo de uso:

template <typename T> 
class Lazy 
{ 
public: 
    void _ctor(bool b); 
    void _ctor(typename __enable_if<!std::is_same<T, bool>::value, T>::type); 
}; 

template <typename T> 
void Lazy<T>::_ctor(bool b) 
{ 
    std::cout << "bool " << b << std::endl; 
}; 

template <typename T> 
void Lazy<T>::_ctor(typename __enable_if<!std::is_same<T, bool>::value, T>::type t) 
{ 
    std::cout << "T " << t << std::endl; 
}; 

int main(int argc, char **argv) 
{ 
    Lazy<int> i; 
    i._ctor(10); 
    i._ctor(true); 

    Lazy<bool> b; 
    b._ctor(true); 

    return 0; 
} 
+0

+1, aunque generalmente trato de evitar los nombres iniciales con guiones bajos porque hay reglas en las que se reservan nombres que comiencen con uno o dos guiones bajos en algunos casos. Además, ¿hay alguna razón por la que haya nombrado una función '_ctor' en lugar de simplemente definir un constructor? – SirGuy

+0

como recuerdo los guiones bajos se usan para mostrar la implementación local de algo, _ctor es solo el nombre del método en mi código que no me molesté en cambiar el nombre – Alexander77

+0

de [cppreference] (http://en.cppreference.com/w/ cpp/keyword): _También, ** todos los identificadores que contienen un doble guión bajo __ en cualquier posición ** y cada identificador que comienza con un guión bajo seguido de una letra mayúscula siempre está reservado y todos los identificadores que comienzan con un guión bajo están reservados para usar como nombres en el espacio de nombres global. Consulte los identificadores para obtener más información. Por lo menos, debe cambiar el nombre de su plantilla '__enable_if' para asegurarse de que nunca interfiera con los detalles de implementación de la biblioteca estándar. – SirGuy

Cuestiones relacionadas