Выбирать ли перегрузку функции шаблона или частичную специализацию функтора (объекта функции)

Ниже приведен отрывок реализации ST ++ для g ++ (версия STL для sgi). Я хочу знать, почему они используют частичную специализацию вместо перегрузки функций.

template <class InputIterator, class OutputIterator>
struct __copy_dispatch
{
OutputIterator operator()(InputIterator first, InputIterator last,
OutputIterator result) {
return __copy(first, last, result, iterator_category(first));
}
};

//If the inputiterator and the outputiterator is all type T
//This is a partial specialization of the generalized version
template <class T>
struct __copy_dispatch<T*, T*>//-----------------------(1)
{
T* operator()(T* first, T* last, T* result) {
typedef typename __type_traits<T>::has_trivial_assignment_operator t;
return __copy_t(first, last, result, t());
}
};

//Strictly speaking this is a partial specialization of the last template function
template <class T>
struct __copy_dispatch<const T*, T*>//-----------------(2)
{
T* operator()(const T* first, const T* last, T* result) {
typedef typename __type_traits<T>::has_trivial_assignment_operator t;
return __copy_t(first, last, result, t());
}
};//The generalized version of copy
template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result)
{
return __copy_dispatch<InputIterator,OutputIterator>()(first, last, result);
}

//A overload version
inline char* copy(const char* first, const char* last, char* result) {
memmove(result, first, last - first);
return result + (last - first);
}

Что если я использую версию с перегрузкой, например:

#include <iostream>

using namespace std;template <class InputIterator, class OutputIterator>
OutputIterator copy_dispatch(InputIterator first, InputIterator last,
OutputIterator result) {
cout << "now in first" << endl;
return result;
}
template <class T>
T* copy_dispatch(T* first, T* last, T* result) {
cout << "now in second" << endl;
return 0;
}
template <class T>
T* copy_dispatch(const T* first, const T* last, T* result) {
cout << "now in third" << endl;
return 0;
}

int main( void ) {
int a[]={1,2,3,4,5,6};
double b[] = {1.0,2.0,3.0,4.0,5.0,6.0};
int c[]={0,0,0,0,0,0};
int const d[]={0,0,0,0,0,0};

copy_dispatch(a,a+6, b);
copy_dispatch(a, a+6, c);
copy_dispatch(d, d+6, c);

}

Выход:

now in first
now in second
now in third

Кажется, это тоже работает нормально?

Поэтому есть ли еще какая-то причина использовать класс функторов с частичной специализацией вместо перегрузки функций?

ОБНОВЛЕНИЕ

Вот некоторые другие выдержки из sgi-реализации STL:

//sgi 4.5
template<bool>
struct _Destroy_aux
{
template<typename _ForwardIterator>
static void
__destroy(_ForwardIterator __first, _ForwardIterator __last)
{
for (; __first != __last; ++__first)
std::_Destroy(&*__first);
}
};

template<>
struct _Destroy_aux<true>
{
template<typename _ForwardIterator>
static void
__destroy(_ForwardIterator, _ForwardIterator) { }

};

//in an old version of sgi 2.9 this is implemented with function overload
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
for ( ; first < last; ++first)
destroy(&*first);
}

template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}

template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*) {
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());

5

Решение

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

Чтобы объединить лучшее из обоих миров, наиболее общий код делает то, что вы показали в своем вопросе

//The generalized version of copy
template <class InputIterator, class OutputIterator>
inline OutputIterator copy(InputIterator first, InputIterator last,
OutputIterator result)
{
return __copy_dispatch<InputIterator,OutputIterator>()(first, last, result);
}

Шаблон функции верхнего уровня имеет свои аргументы (что помогает пользователям писать компактный код), а шаблон внутреннего класса специализирован для различных особых случаев (что помогает компилятору включать оптимизации). Оболочка функции верхнего уровня вокруг объекта функции внутреннего уровня встроена каждым достойным компилятором, поэтому никаких накладных расходов нет.

ОБНОВИТЬ: Как вы заметили себя, технически, Вы можете добиться того же эффекта, заменив частичную специализацию шаблона класса перегрузкой шаблона функции, и явную специализацию шаблона класса обычной функцией (вместо разрешенной явной специализации шаблона функции, которая, как объясняется в столбце Саттера, не будет участвовать в разрешении перегрузки). ).

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

3

Другие решения

Это в основном вопрос предпочтений. Любую диспетчеризацию во время компиляции для вызова функции, которая может быть сделана с частичной специализацией шаблона класса, вы могли бы также сделать с перегрузками шаблона функции, и наоборот. Фактически, Стандарт определяет частичное упорядочение в частичных специализациях шаблона класса, ссылаясь на частичное упорядочение в перегруженных шаблонах функций (14.5.5.2).

Тем не менее, иногда вы хотите, чтобы выбор во время компиляции управлял чем-то другим, а не только какой функцией вызывать. Шаблоны классов могут иметь любое количество функций-членов и определения типов членов, а не только одну функцию. И до C ++ 11 constexprчлен статического класса был безусловно лучшим способом установить константное выражение, зависящее от параметров шаблона, чтобы его специализации можно было использовать в качестве параметров шаблона, границ массива и так далее.

2

Я бы добавил, что в вашем решении есть «проблема»

template <class T> void foo(T t) { std::cout << "foo<T>" << std::endl; }   // (a)
template <class T> void foo(T* t) { std::cout << "foo<T*>" << std::endl; } // (b)

void bar() {
int i = 0;

foo(i);          // call (a) f<int>(int)
foo(&i);         // call (b) f<int>(int*)
foo<int*>(&i);   // call (a) f<int*>(int*) and not (b)
}

Так что в вашем случае, если я позвоню

copy_dispatch<char*, char*>

Я не получаю специализацию, как в STL.

0
По вопросам рекламы [email protected]