Ограничить функцию шаблона - PullRequest
16 голосов
/ 29 сентября 2008

Я написал пример программы на 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;
}

Ответы [ 7 ]

20 голосов
/ 29 сентября 2008

Это возможно с помощью 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;
}
19 голосов
/ 29 сентября 2008

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

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;
18 голосов
/ 29 сентября 2008

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

Итак, вы создаете с помощью 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, чтобы сделать его проверкой во время компиляции вместо проверки во время выполнения

3 голосов
/ 29 сентября 2008

Вот как ты это делаешь.

Например, прокомментируйте специализацию шаблона для double, и это не позволит вам вызывать эту функцию с параметром double. Хитрость заключается в том, что если вы попытаетесь вызвать sum с типом, который не относится к специализациям 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;
}
1 голос
/ 29 сентября 2008

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

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

1 голос
/ 29 сентября 2008

Вы можете изучить черты типа (используйте boost, подождите C ++ 0x или создайте свой собственный).

Я нашел следующее в Google: http://artins.org/ben/programming/mactechgrp-artin-cpp-type-traits.pdf

1 голос
/ 29 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...