Специализация шаблона VS Перегрузка функций - PullRequest
47 голосов
/ 18 августа 2011

Учебник У меня есть примечания, что вы можете предоставить собственную реализацию стандартных библиотечных функций, таких как swap(x,y), через специализацию шаблонов для перегрузки функций. Это было бы полезно для любых типов, которые могут получить выгоду от чего-то другого, кроме свопа присваивания, например STL containers (в котором уже написаны свопы, я знаю).

Мои вопросы:

  1. Что лучше: специализация на шаблонах реализация подкачки или перегрузка функций, обеспечивающая точное параметры, которые вы хотите использовать без шаблона?

  2. Почему это лучше? Или если они равны, почему это?

Ответы [ 3 ]

67 голосов
/ 18 августа 2011

Краткая история: перегружайте, когда можете, специализируйте, когда вам нужно.

Длинная история: C ++ относится к специализации и перегружается совсем по-другому. Это лучше всего объяснить на примере.

template <typename T> void foo(T);
template <typename T> void foo(T*); // overload of foo(T)
template <>           void foo<int>(int*); // specialisation of foo(T*)

foo(new int); // calls foo<int>(int*);

Теперь давайте поменяемся местами последние два.

template <typename T> void foo(T);
template <>           void foo<int*>(int*); // specialisation of foo(T)
template <typename T> void foo(T*); // overload of foo(T)

foo(new int); // calls foo(T*) !!!

Компилятор выполняет разрешение перегрузки, даже не обращая внимания на специализации. Таким образом, в обоих случаях разрешение перегрузки выбирает foo(T*). Однако, только в первом случае он находит foo<int*>(int*), потому что во втором случае специализация int* является специализацией foo(T), а не foo(T*).


Вы упомянули std::swap. Это еще более усложняет ситуацию.

Стандарт гласит, что вы можете добавить специализации в пространство имен std. Отлично, у вас есть какой-то тип Foo и у него есть производительный своп, тогда вы просто специализируете swap(Foo&, Foo&) в пространстве имен std. Нет проблем.

Но что, если Foo класс шаблона? C ++ не имеет частичной специализации функций, поэтому вы не можете специализироваться swap. Ваш единственный выбор - перегрузка, но стандарт гласит, что вы не можете добавлять перегрузки в пространство имен std!

На данный момент у вас есть два варианта:

  1. Создайте функцию swap(Foo<T>&, Foo<T>&) в своем собственном пространстве имен и надеемся, что она будет найдена через ADL. Я говорю «надежда», потому что если стандартная библиотека вызывает swap, как std::swap(a, b);, то ADL просто не будет работать.

  2. Игнорировать часть стандарта, в которой говорится не добавлять перегрузки и делать это в любом случае. Честно говоря, даже если это технически запрещено, во всех реалистичных сценариях это сработает.

Однако следует помнить, что нет никакой гарантии, что стандартная библиотека вообще использует swap. Большинство алгоритмов используют std::iter_swap, а в некоторых реализациях, на которые я смотрел, он не всегда перенаправляет на std::swap.

12 голосов
/ 24 февраля 2015

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

Например,

template<class T> T zero();
template<> int zero() { return 0; }
template<> long zero() { return 0L; }

Чтобы сделать что-то подобное, используя перегрузку функции, вам необходимо добавить параметр в сигнатуру функции:

int zero(int) { return 0; }
long zero(long) { return 0L; }
5 голосов
/ 18 августа 2011

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

Другой вариант - поместить вашу функцию swap в то же пространство имен, в котором она работает, и using std::swap; перед вызовом неквалифицированного свопа.

...