2008-09-29 15 views
15

Escribí un programa de ejemplo en http://codepad.org/ko8vVCDF que usa una función de plantilla.Restringir la función de plantilla

¿Cómo puedo retrotraer la función de la plantilla para que solo use números? (int, doble, etc.)

#include <vector> 
#include <iostream> 

using namespace std; 

    template <typename T> 
T sum(vector<T>& a) 
{ 
    T result = 0; 
    int size = a.size(); 
    for(int i = 0; i < size; i++) 
    { 
     result += a[i]; 
    } 

    return result; 
} 

int main() 
{ 
    vector<int> int_values; 
    int_values.push_back(2); 
    int_values.push_back(3); 
    cout << "Integer: " << sum(int_values) << endl; 

    vector<double> double_values; 
    double_values.push_back(1.5); 
    double_values.push_back(2.1); 
    cout << "Double: " << sum(double_values); 

    return 0; 
} 
+2

Una coma sería útil en su pregunta. –

+1

@ DanielRodríguez: para ser justos, había una coma allí ... – sehe

+0

¿Qué es un "número"? – curiousguy

Respuesta

17

La única manera de restringir una plantilla es hacerlo de modo que utilice algo de los tipos que desee, que otros tipos no tienen.

Así, se construye con un int, el uso + y + =, llama a un constructor de copia, etc.

Cualquier tipo que tiene todas estas trabajará con su función - por lo que, si se crea una nueva tipo que tiene estas características, su función funcionará en él, que es genial, ¿no?

Si desea restringirlo más, use más funciones que solo están definidas para el tipo que desea.

Otra forma de implementar esto es mediante la creación de una plantilla rasgos - algo como esto

template<class T> 
SumTraits 
{ 
public: 
    const static bool canUseSum = false; 
} 

Y luego se especializan para las clases que desea estar bien:

template<> 
class SumTraits<int> 
{ 
    public: 
    const static bool canUseSum = true; 
}; 

Luego, en su código, puede escribir

if (!SumTraits<T>::canUseSum) { 
    // throw something here 
} 

edición: como se ha mencionado en los comentarios, puede utilizar B OOST_STATIC_ASSERT para convertirlo en una verificación en tiempo de compilación en lugar de en tiempo de ejecución

+3

También puede usar BOOST_STATIC_ASSERT para aplicar la condición en tiempo de compilación. – ChrisN

+0

Esto es un control en tiempo de ejecución, lo cual no es una buena idea ya que esto se puede hacer en tiempo de compilación. –

+0

SFINAE es la solución a esto, no BOOST_STATIC_ASSERT –

1

¿Por qué quiere restringir los tipos en este caso? Las plantillas permiten "tipado de pato estático", por lo que cualquier cosa permitida por su función sum en este caso debería permitirse. Específicamente, la única operación requerida de T es la asignación de asignación y la inicialización por 0, por lo que cualquier tipo que admita esas dos operaciones funcionaría. Esa es la belleza de las plantillas.

(Si ha cambiado su initialiser a T result = T(); o similar, entonces sería trabajar para ambos números y cadenas, también.)

+1

Hazlo más seguro. –

+1

Para ampliar "type safe", diría "para evitar tener que documentar y atenerme a un conjunto limitado de suposiciones que hago sobre el comportamiento de los operadores aritméticos y las conversiones, que los tipos no incorporados pueden no satisfacer" . De lo contrario, podría cambiar la implementación en el futuro y desear más suposiciones. –

+1

Entonces, por ejemplo, actualmente usa + =. Si más tarde cambia para usar +, entonces un usuario cuyo tipo solo admite + = vendrá a ser un recortador. Para un ejemplo así de simple, no puedo pensar en ningún otro cambio plausible de implementación, y sobrecargar + = y no + es pedir problemas de todos modos, pero con suerte yswim. –

18

se puede hacer algo como esto:

template <class T> 
class NumbersOnly 
{ 
private: 
    void ValidateType(int &i) const {} 
    void ValidateType(long &l) const {} 
    void ValidateType(double &d) const {} 
    void ValidateType(float &f) const {} 

public: 
    NumbersOnly() 
    { 
     T valid; 
     ValidateType(valid); 
    }; 
}; 

obtendrá un error si se intenta crear una NumbersOnly que no tiene una sobrecarga de ValidateType :

NumbersOnly<int> justFine; 
NumbersOnly<SomeClass> noDeal; 
+0

Buen trabajo. http://lenniedevilliers.codepad.org/aR6xi14c –

+4

Pensé brevemente que esto estaba mal (lo siento), pero las firmas & en el método son vitales - previene la creación de instancias con un tipo que solo es convertible a int. Así que char, unsigned int y una clase que sobrecarga el operador int() están prohibidas como se esperaba. –

+1

Como es "largo doble", que podría no ser lo que quieres. Pero eso es solo cuestión de enumerar todos los tipos aritméticos del estándar C++. –

1

De hecho, no hay necesidad de hacerlo más estricto. Eche un vistazo a la versión de cadena (usando el estilo de constructor predeterminado aconsejado por Chris Jester-Young) here ...

Tenga cuidado, también, para desbordamientos: puede necesitar un tipo más grande para contener resultados intermedios (o resultados de salida) Bienvenido al ámbito de la metaprogramación, entonces :)

2

Así es como lo hace.

Comenta la especialización de la plantilla para el doble, por ejemplo ... y no te permitirá llamar a esa función con el doble como parámetro. El truco es que si intentas llamar a la suma con un tipo que no está entre las especializaciones de IsNumber, entonces se llama a la implementación genérica, y esa implementación hace algo no permitido (llamar a un constructor privado).

El mensaje de error NO es intuitivo a menos que cambie el nombre de la clase IsNumber a algo que suene como un mensaje de error.

#include <vector> 
#include <iostream> 

using namespace std; 

template<class T> struct IsNumber{ 
private: 
IsNumber(){} 
}; 

template<> struct IsNumber<float>{ 
    IsNumber(){}; 
}; 

template<> struct IsNumber<double>{ 
    IsNumber(){}; 
}; 

template<> struct IsNumber<int>{ 
    IsNumber(){}; 
}; 

template <typename T> 
T sum(vector<T>& a) 
{ 
IsNumber<T> test; 
T result = 0; 
int size = a.size(); 
for(int i = 0; i < size; i++) 
{ 
    result += a[i]; 
} 

return result; 
} 




int main() 
{ 
vector<int> int_values; 
int_values.push_back(2); 
int_values.push_back(3); 
cout << "Integer: " << sum(int_values) << endl; 

vector<double> double_values; 
double_values.push_back(1.5); 
double_values.push_back(2.1); 
cout << "Double: " << sum(double_values); 

return 0; 
} 
20

Esto es posible mediante el uso de SFINAE, y hace más fácil mediante el uso de ayudantes de cualquiera de Boost o C++ 11

Boost:

#include <vector> 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits/is_arithmetic.hpp> 

template<typename T> 
    typename boost::enable_if<typename boost::is_arithmetic<T>::type, T>::type 
     sum(const std::vector<T>& vec) 
{ 
    typedef typename std::vector<T>::size_type size_type; 
    T result; 
    size_type size = vec.size(); 
    for(size_type i = 0; i < size; i++) 
    { 
    result += vec[i]; 
    } 

    return result; 
} 

C++ 11:

#include <vector> 
#include <type_traits> 

template<typename T> 
    typename std::enable_if<std::is_arithmetic<T>::value, T>::type 
     sum(const std::vector<T>& vec) 
{ 
    T result; 
    for (auto item : vec) 
    result += item; 
    return result; 
} 
+1

' vector :: :: 'hum ... – curiousguy

+0

@curiousguy: en este sniplet asumí el include (vector) y los usos (std) del OP. –

+0

@LeonTimmermans Entonces? – curiousguy

Cuestiones relacionadas