Ограничить функцию шаблона
Я написал образец программы в http://codepad.org/ko8vVCDF, который использует функцию шаблона.
Как получить функцию шаблона только для использования чисел? (int, double и т.д.)
#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;
}
Ответы
Ответ 1
Единственный способ ограничить шаблон - сделать так, чтобы он использовал что-то из тех типов, которые вы хотите, чтобы другие типы не имели.
Итак, вы строите с помощью int, используйте + и + =, вызовите конструктор копирования и т.д.
Любой тип, который имеет все это, будет работать с вашей функцией - поэтому, если я создаю новый тип, который имеет эти функции, ваша функция будет работать на нем - что здорово, не так ли?
Если вы хотите ограничить его больше, используйте больше функций, которые только определены для нужного вам типа.
Другим способом реализации этого является создание шаблона признаков - что-то вроде этого
template<class T>
SumTraits
{
public:
const static bool canUseSum = false;
}
И затем специализируйте его для классов, которые вы хотите быть в порядке:
template<>
class SumTraits<int>
{
public:
const static bool canUseSum = true;
};
Затем в вашем коде вы можете написать
if (!SumTraits<T>::canUseSum) {
// throw something here
}
edit: как указано в комментариях, вы можете использовать BOOST_STATIC_ASSERT, чтобы сделать его временем проверки компиляции вместо времени выполнения
Ответ 2
Это возможно, используя SFINAE и упростившись с помощью помощников из Boost или С++ 11
Повышение:
#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;
}
С++ 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;
}
Ответ 3
Вы можете сделать что-то вроде этого:
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 );
};
};
Вы получите сообщение об ошибке, если попытаетесь создать NumbersOnly, у которого нет перегрузки ValidateType:
NumbersOnly<int> justFine;
NumbersOnly<SomeClass> noDeal;
Ответ 4
Вот как вы это делаете.
Обратите внимание на специализацию шаблона для double, например.. и он не позволит вам вызывать эту функцию с двойным параметром. Хитрость заключается в том, что если вы попытаетесь вывести сумму с типом, который не относится к специализациям IsNumber
, тогда вызывается общая реализация, и эта реализация делает что-то недопустимым (вызывает частный конструктор).
Сообщение об ошибке НЕ является интуитивным, если вы не переименуете класс IsNumber
в сообщение, которое звучит как сообщение об ошибке.
#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;
}
Ответ 5
Почему вы хотите ограничить типы в этом случае? Шаблоны позволяют "статическую утиную печать", поэтому все, что разрешено, в соответствии с вашей функцией sum
в этом случае должно быть разрешено. В частности, единственной операцией, требуемой для T
, является добавление и инициализация на 0, поэтому любой тип, поддерживающий эти две операции, будет работать. Это красота шаблонов.
(Если вы изменили свой инициализатор на T result = T();
или тому подобное, то он будет работать и для чисел, и для строк).
Ответ 6
Вы можете посмотреть на черты типов (используйте boost, подождите С++ 0x или создайте свой собственный).
Я нашел следующее в google: http://artins.org/ben/programming/mactechgrp-artin-cpp-type-traits.pdf
Ответ 7
В самом деле, нет необходимости делать его более строгим. Посмотрите на строковую версию (используя стиль конструктора по умолчанию, рекомендованный Крисом Джеттером-Яном) здесь...
Соблюдайте осторожность при переполнении - вам может потребоваться больший тип, чтобы содержать промежуточные результаты (или результаты вывода). Добро пожаловать в область метапрограммирования, затем:)