2016-09-15 2 views
3

Я создал две функции шаблона, которые получают суммы для разных контейнеров STL: один для списка и векторный, другой для карты.Перегрузка шаблона функции vs Явная специализация

Пожалуйста, просмотрите прокомментированную строку (1) и (2) определения второй функции шаблона. Закомментированный код (= 2) также отлично работает, поэтому я не знаю, какой из них более рекомендуется.

Также, как называется каждый метод (я точно угадал в своем комментарии)? Сказать (1) - перегрузка шаблона функции, кажется, недостаточна, поскольку после ключевого слова «шаблон» отсутствует аргумент имени типа. То есть, это должно быть как template<typename T> в сочетании с (1) для того, чтобы метод вызывался как перегрузка шаблона функции, я думаю. Пожалуйста, дайте мне свое имя.

template <typename T> // T : container 
double Sum(const T &l) // get Container 
{ 
    double sum = 0; 
    T::const_iterator i; 
    for (i = l.begin(); i != l.end(); ++i) { sum += *i; } 
    return sum; 
} 

template <> // get container 
double Sum(const map<string, double> &m) // (1) function template overloading 
// double Sum<map<string, double> >(const map<string, double> &m) // (2) explicit specialization 
{ 
    double sum = 0; 
    map<string, double>::const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 
+0

No (1) является шаблоном специализация и (2) является перегрузкой суммы. – coincoin

ответ

1

Оба явный синтаксис специализации

  • template <> double Sum(const map<string, double> &m);

  • template <> double Sum<map<string, double> >(const map<string, double> &m)

Первый пусть компилятор вывести параметр, в то время как второй, вы явно.

Второй требуется, когда компилятор не может вывести все параметры шаблона, как для

template <typename T> std::string getNameType(); 

template <> std::string getNameType<int>() { return "int"; } 

или неоднозначность, которые функционируют шаблон специализироваться

template <typename T> void foo(T); 

template <typename T> void foo(T*); // overload, not specialization 

//template <> void foo(int*); // Error: do you mean T = int for foo(T*) or T = int* for foo(T) 

template <> void foo<int*>(int*); // specialize foo(T) 
template <> void foo<int>(int*); // specialize foo(T*) 

Это, как правило, лучше использовать перегрузку вместо специализации для функция, поэтому для вашего примера:

template <typename Key> 
double Sum(const std::map<Key, double> &m) 
{ 
    double sum = 0; 
    for (const auto& p : m) { sum += p.second; }  return sum; 
} 
+0

Большое вам спасибо за подробный и полный ответ. Это действительно помогает. –

0

Для конкретного типа, вы, вероятно, лучше просто определения перегрузки без шаблона:

double Sum(const std::map<std::string, double> &m) // (1) function template overloading 
// double Sum<map<string, double> >(const map<string, double> &m) // (2) explicit specialization 
{ 
    double sum = 0; 
    std::map<std::string, double>::const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 

Шаблоны более полезны для общих функций:

// 
// sum any map's arithmetic mapped values 
// 
template<class K, class V, class C, class A> 
typename std::map<K, V, C, A>::mapped_type 
Sum(const std::map<K, V, C, A> &m) // (1) function template overloading 
{ 
    using map_type = std::map<K, V, C, A>; 
    using mapped_type = typename map_type::mapped_type; 
    using const_iterator = typename map_type::const_iterator; 
    mapped_type sum = 0; 
    const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 

.. или для получения педантично-общего характера (C++ 14) ...

namespace notstd { 
    template<class...> 
    using void_t = void; 
} 

template< class, class = notstd::void_t<> > 
struct supports_mapped_type : std::false_type { }; 

template< class T > 
struct supports_mapped_type<T, notstd::void_t<typename T::mapped_type>> : std::true_type { }; 

template< class, class = notstd::void_t<> > 
struct supports_const_iterator : std::false_type { }; 

template< class T > 
struct supports_const_iterator<T, notstd::void_t<typename T::const_iterator>> : std::true_type { }; 

template<class T> static constexpr bool IsMaplike = supports_mapped_type<T>() and supports_const_iterator<T>(); 

template<class MapLike, 
std::enable_if_t< 
IsMaplike<MapLike> and std::is_arithmetic<typename MapLike::mapped_type>() 
>* = nullptr> 
typename MapLike::mapped_type 
Sum(const MapLike &m) // (1) function template overloading 
{ 
    using map_type = MapLike; 
    using mapped_type = typename map_type::mapped_type; 
    using const_iterator = typename map_type::const_iterator; 
    mapped_type sum = 0; 
    const_iterator i; // obtain Iterator from Container 
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data) 
    return sum; 
} 

Это теперь счастливо просуммировать:

std::unordered_map<std::string, int> и std::map<std::string, int>,

но не std::map<int, std::string>

+0

Я узнал многое из вашего ответа. Большое спасибо! –

+0

@ DongkyuChoi, это идея. Добро пожаловать :) –

0

Я не совсем понял ваш вопрос, так как ваш код, кажется, хорошо, но я Я попытаюсь ответить. Перегрузка функций - это подход, когда вы вручную записываете несколько функций с тем же именем, но с разными типами аргументов. Например:

double Sum(const std::vector<double>& l) { 
    //... 
} 

double Sum(const std::list<double>& l) { 
    //... 
} 

double Sum(const std::deque<double>& l) { 
    //... 
} 

В вашем примере вы написали шаблон функции:

template <typename T> 
double Sum(const T &l) //... 

и специализации шаблона:

template <> 
double Sum(const map<string, double> &m) //... 

Что лучше? Это зависит от вашей ситуации. См., С перегрузкой функции, вы должны написать код самостоятельно, в то время как в случае шаблонов компилятор сделает это за вас!

Например, ваш шаблон общего случая будет работать vector, list, queue, deque и любой другой совместимый контейнер, который может даже не существовать на момент создания шаблона. Компилятор генерирует код только для тех типов, которые используются для создания экземпляра шаблона. Если вы попытаетесь создать экземпляр с несовместимым типом, вы получите ошибку компиляции. И только если вы создаете экземпляр с помощью map<string, double> (что недопустимо для шаблона общего случая), компиляция будет успешной, так как специализация будет выбрана для генерации кода.

Как упомянуто @RichardHodges, специализация может быть излишним для вашего дела; должно быть достаточно перегрузки без шаблонов.

+0

Ваше объяснение было очень легко понять. Большое спасибо за ваш ответ! –

Смежные вопросы