Различия между шаблонной специализацией и перегружающийся для функций?

Тот в http://www.developerfusion.com/tools/convert/csharp-to-vb/ (новый URL) теперь поддерживает.NET 3,5 синтаксиса (благодаря #develop парням еще раз) и автоматически скопирует результаты в Ваш буфер обмена:)

21
задан rlbond 2 October 2009 в 21:54
поделиться

5 ответов

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

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
template <> int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses explicit specialization

// --- against ---

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses template

Это потому, что специализации обнаруживаются не путем поиска имени, а путем сопоставления аргументов, поэтому объявление using автоматически учитывает введенную позже специализацию.

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

template <typename T> void f(T t); // called for non-pointers
template <typename T> void f(T *t); // called for pointers.

int a;
void e() {
  f(a); // calls the non-pointer version
  f(&a); // calls the pointer version
}

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

template<typename T> void f(T const &);
template<> void f(int * const &);

template<typename T> void g(T const &);
void g(int * const &);

int a[5];
void e() {
  // calls the primary template, not the explicit specialization
  // because `T` is `int[5]`, not `int *`
  f(a);

  // calls the function, not the template, because the function is an
  // exact match too (pointer conversion isn't costly enough), and it's 
  // preferred. 
  g(a);
}

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

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

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

14
ответ дан 29 November 2019 в 21:41
поделиться

Один из таких примеров:

#include <cstdio>

template <class T>
void foo(T )
{
    puts("T");
}

//template <>
void foo(int*)
{
    puts("int*");
}

template <class T>
void foo(T*)
{
    puts("T*");
}

int main()
{
    int* a;
    foo(a);
}

Фактически предлагается использовать не шаблонные перегрузки для функций и оставить специализацию для классов. Более подробно это обсуждается в Почему не специализировать шаблоны функций?

4
ответ дан 29 November 2019 в 21:41
поделиться

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

ОБНОВЛЕНИЕ: Чтобы уточнить, в комментариях AraK, вы действительно сравниваете яблоки и апельсины здесь. Перегрузка функций используется для предоставления возможности иметь разные функции с одним именем, если они имеют разные подписи. Специализация шаблона используется для определения определенного фрагмента кода для конкретного параметра типа. У вас не может быть специализации шаблона, если у вас нет шаблона. Если вы удалите первый фрагмент кода, который объявляет универсальный шаблон, вы получите ошибку времени компиляции, если попытаетесь использовать специализацию шаблона.

Итак, цель специализации шаблона сильно отличается от перегрузки функции. Просто в вашем примере они ведут себя одинаково, хотя принципиально отличаются друг от друга.

Если вы предоставляете перегрузку, вы объявляете независимый метод с тем же именем. Вы не запрещаете использовать шаблон с определенным параметром типа. Чтобы продемонстрировать этот факт, попробуйте:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

int inc(const int& t)
{
    return t + 42;
}

#include <iostream>
int main() {
   int x = 0;
   x = inc<int>(x);
   std::cout << "Template: " << x << std::endl; // prints 1.

   x = 0;
   x = inc(x);
   std::cout << "Overload: " << x << std::endl; // prints 42.
}

Как видите, в этом примере есть две различные функции inc для значений int : inc (const int &) и inc (const int &) . Вы не смогли бы расширить общий шаблон с помощью int , если бы вы использовали специализацию шаблона.

Если вы предоставляете перегрузку, вы объявляете независимый метод с тем же именем. Вы не запрещаете использовать шаблон с определенным параметром типа. Чтобы продемонстрировать этот факт, попробуйте:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

int inc(const int& t)
{
    return t + 42;
}

#include <iostream>
int main() {
   int x = 0;
   x = inc<int>(x);
   std::cout << "Template: " << x << std::endl; // prints 1.

   x = 0;
   x = inc(x);
   std::cout << "Overload: " << x << std::endl; // prints 42.
}

Как видите, в этом примере есть две различные функции inc для значений int : inc (const int &) и inc (const int &) . Вы не смогли бы расширить общий шаблон с помощью int , если бы вы использовали специализацию шаблона.

Если вы предоставляете перегрузку, вы объявляете независимый метод с тем же именем. Вы не запрещаете использовать шаблон с определенным параметром типа. Чтобы продемонстрировать этот факт, попробуйте:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

int inc(const int& t)
{
    return t + 42;
}

#include <iostream>
int main() {
   int x = 0;
   x = inc<int>(x);
   std::cout << "Template: " << x << std::endl; // prints 1.

   x = 0;
   x = inc(x);
   std::cout << "Overload: " << x << std::endl; // prints 42.
}

Как видите, в этом примере есть две различные функции inc для значений int : inc (const int &) и inc (const int &) . Вы не смогли бы расширить общий шаблон с помощью int , если бы вы использовали специализацию шаблона.

существуют две различные функции inc для значений int : inc (const int &) и inc (const int &) . Вы не смогли бы расширить общий шаблон с помощью int , если бы вы использовали специализацию шаблона.

существуют две различные функции inc для значений int : inc (const int &) и inc (const int &) . Вы не смогли бы расширить общий шаблон с помощью int , если бы вы использовали специализацию шаблона.

5
ответ дан 29 November 2019 в 21:41
поделиться

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

1
ответ дан 29 November 2019 в 21:41
поделиться

Just to elaborate on the first point mentioned by litb in his answer. Specializations are only checked once overload resolution has actually selected a primary template. The result can lead to some surprises where a function is overloaded and has explicit specializations:

template <typename T> void foo (T);  // Primary #1
template <> void foo<int*> (int*);   // Specialization of #1

template <typename T> void foo (T*); // Primary #2

void bar (int * i)
{
  foo(i);
}

When choosing which function to call, the following steps take place:

  1. Name lookup finds both primary templates.
  2. Each template is specialized and overload resolution attempts to select a best function based on conversions between the arguments and parameters.
  3. In thise case, there is no difference in the quality of the conversions.
  4. Partial ordering rules are then used to select the most specialized template. In this case that is the second parimary "foo(T*)".

Only after these steps, when the best function has been selected will explicit specializations of the selected function be considered. (In this case primary #2 has none so none are considered).

The only way to call the above explicit specialization here, is to actually use explicit template arguments in the call:

void bar (int * i)
{
  foo<int*> (i);  // expliit template argument forces use of primary #1
}

A good rule of thumb is to try to avoid having overloads that are also explicily specialized, as the resulting rules are pretty complex.

1
ответ дан 29 November 2019 в 21:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: