2008-09-29 2 views
15

Я написал образец программы в http://codepad.org/ko8vVCDF, который использует функцию шаблона.Restrict Template Function

Как получить функцию шаблона только для использования чисел? (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; 
} 
+2

В вашем вопросе будет полезной запятая. – 2009-11-12 17:23:35

+1

@ DanielRodríguez: чтобы быть справедливым, там была запятая там ... – sehe 2011-12-02 16:17:16

+0

Что такое «номер»? – curiousguy 2011-12-03 06:35:43

ответ

17

Единственный способ ограничить шаблон - сделать так, чтобы он использовал что-то из тех типов, которые вы хотите, других типов нет.

Итак, вы строите с ИНТ, используйте + и + =, вызвать конструктор копирования и т.п.

Любого типа, который имеет все это будет работать с вашей функцией - так, если я создаю новый тип, который имеет эти функции, ваша функция будет работать на нем - это здорово, не так ли?

Если вы хотите ограничить его, используйте больше функций, которые только определены для нужного вам типа.

Другой способ реализовать это путем создания шаблона черт - что-то вроде этого

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 
} 

изменить: как указано в комментариях, вы можете использовать B OOST_STATIC_ASSERT, чтобы сделать это во время компиляции, а не во время выполнения.

1

Почему вы хотите ограничить типы в этом случае? Шаблоны позволяют «статическую утиную печать», поэтому все, что разрешено с помощью функции sum, в этом случае должно быть разрешено. В частности, единственной операцией, требуемой от T, является добавление и инициализация на 0, поэтому любой тип, поддерживающий эти две операции, будет работать. Это красота шаблонов.

(Если вы изменили Инициализатора к T result = T(); или тому подобное, то он будет работать для обоих чисел и строк, тоже.)

18

Вы можете сделать что-то вроде этого:

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

Действительно, нет необходимости делать его более строгим. Посмотрите на строковую версию (используя стиль конструктора по умолчанию, рекомендованный Крисом Джеттером-Яном) here ...

Также заботитесь о переполнении - вам может понадобиться больший тип, чтобы содержать промежуточные результаты (или результаты вывода). Добро пожаловать в область метапрограммирования, затем :)

2

Вот как вы это делаете.

Комментировать специализацию шаблона для двойного, например .. и он не позволит вам вызывать эту функцию с двойным параметром. Фокус в том, что если вы попытаетесь вывести сумму с типом, который не относится к специализациям 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; 
} 
20

Это возможно с помощью SFINAE и облегчен с помощью помощников либо из Boost, или C++ 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; 
} 

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