Разница между явной специализацией и регулярными функциями при перегрузке функции шаблона

Сегодня я на ролике. Здесь идет n00b вопрос номер 7:

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

Какую подходящую ситуацию использовать явную специализацию? Я не совсем понимаю:

#include <iostream>

template <typename s> void test(s var1);
template <> void test<int>(int var1);

int main(){
    test(1);
    test(1.1);
    test("hello!!");
    return 0;
}

template <typename s> void test(s var1){
    std::cout << var1 << std::endl;
}

template <> void test<int>(int var1){
    std::cout << "int " << var1 << std::endl;
}

Как противопоставить:

#include <iostream>

template <typename s> void test(s var1);
void test(int var1);

int main(){
    test(1);
    test(1.1);
    test("hello!!");
    return 0;
}

template <typename s> void test(s var1){
    std::cout << var1 << std::endl;
}

void test(int var1){
    std::cout << "int " << var1 << std::endl;
}

Ответы

Ответ 1

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

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

Ответ 2

Основное различие: Явные специализации вообще не участвуют в перегрузке.

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

Вызов с помощью f("hello") не будет рассматривать какие-либо явные специализации. Он будет принимать только все шаблоны и выводить их аргументы шаблона. Вышеуказанный T будет выведен на char[6], и поэтому специализация не будет выбрана.

Если вы перегружаете шаблон функции, вы имеете совершенно разные характеристики.

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

Вызвав это, он выберет вторую функцию, так как оба параметра (char const(&)[6]) сгенерированной специализации и параметр (char const * const&) соответствуют аргументу одинаково хорошо, но вторая функция является функцией без шаблона, следовательно, это предпочтительно в конечном итоге.

Ответ 3

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

Ответ 4

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

Другое использование для явной специализации - это когда вы хотите "перегрузить" функцию, которая не принимает никаких аргументов. Вы можете использовать явную специализацию, чтобы дать разные определения функции для обработки разных типов данных.

Ответ 5

IMHO, следует использовать явную специализацию для function template, когда вы собираетесь вызывать эту функцию с использованием явного аргумента шаблона. например.

test<int>(myClass); // no argument for 'int' -> declare test() as template specialization

В других случаях вы всегда должны использовать обычную специализацию. например.

test(10, myClass); // argument 10 is 'int' -> test() should be normal specialized

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