c++ - smart - Restringir la función de plantilla
solidity español (7)
¿Por qué querría restringir los tipos en este caso? Las plantillas permiten "tipado de pato estático", por lo que cualquier cosa permitida por lo que debe permitirse su función de sum
en este caso. 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 cambió su inicializador a T result = T();
o similar, entonces funcionaría tanto para números como para cadenas.)
Escribí un programa de ejemplo en http://codepad.org/ko8vVCDF que usa una función de plantilla.
¿Cómo recomienzo la función de plantilla para usar solo 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;
}
Así es como lo haces.
Comente la especialización de la plantilla para el doble, por ejemplo ... y no le 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;
}
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) aquí ...
Tenga cuidado, también, para desbordamientos: es posible que necesite un tipo más grande para contener resultados intermedios (o resultados de salida). Bienvenido al ámbito de la metaprogramación, entonces :)
Podrías buscar rasgos de tipo (usa boost, espera por C ++ 0x o crea el tuyo).
Encontré lo siguiente en google: http://artins.org/ben/programming/mactechgrp-artin-cpp-type-traits.pdf
Puedes 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 intenta crear un NumbersOnly que no tenga una sobrecarga ValidateType:
NumbersOnly<int> justFine;
NumbersOnly<SomeClass> noDeal;
La única forma de restringir una plantilla es hacerla de modo que use algo de los tipos que desee, que otros tipos no tienen.
Entonces, construyes con un int, usa + y + =, llamas a un constructor de copia, etc.
Cualquier tipo que tenga todo esto funcionará con su función, por lo tanto, si creo un nuevo tipo que tenga estas características, su función funcionará, lo cual 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 creando una plantilla de rasgos, algo como esto
template<class T>
SumTraits
{
public:
const static bool canUseSum = false;
}
Y luego especialícela para las clases que quiere que esté bien:
template<>
class SumTraits<int>
{
public:
const static bool canUseSum = true;
};
Luego, en tu código, puedes escribir
if (!SumTraits<T>::canUseSum) {
// throw something here
}
editar: como se menciona en los comentarios, puede usar BOOST_STATIC_ASSERT para que sea una verificación en tiempo de compilación en lugar de una de tiempo de ejecución
Esto es posible mediante el uso de SFINAE , y se facilita mediante el uso de ayudantes de Boost o C ++ 11
Aumentar:
#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;
}