2014-12-03 2 views
7

Предположит следующее:Явные специализированные функции шаблонов с перегрузками: зачем вам это делать?

template <typename T> void foo (T*); // #1 
template <typename T> void foo (T); // #2 
template <> void foo (int*);   // #3 

При введении явной специализации базового шаблона, который также имеет перегрузки, специализация не рассматриваются при разрешении перегрузки по конструкции. Я понимаю это.

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

BTW Я только пересмотрел правила для C++ 03. Я не уверен, что если C++ 11 изменяет эти правила/поведение.

+4

обязательное чтение: [GotW # 49 - Специализация и перегрузка шаблонов] (http://www.gotw.ca/gotw/049.htm) - TL; DR Не делайте этого. – sehe

+1

Это не дубликат. Я спрашиваю, когда вы * будете использовать явные специализированные функции в сочетании с перегрузкой. Или, если это не должно быть сделано. Ответы связанного вопроса НЕ решают этот вопрос. –

+2

Возможно, в коде есть нешаблон, основанный на foo, который принимает int *. Добавление специализации шаблона означает, что вы можете быть явным и использовать foo (&i); – cppguy

ответ

1

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

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

template <typename T> void foo (T*){std::cout << 1 << std::endl;} // #1 
template <typename T> void foo (T){std::cout << 2 << std::endl;} // #2 
template <> void foo<int> (int* x){std::cout << 3 << std::endl;} // #3 
//void foo (int*){std::cout << 3 << std::endl;}   // #3 

template <typename T> 
void bar(void* x) { 
    foo<T>(reinterpret_cast<T*>(x)); 
    foo<T*>(reinterpret_cast<T*>(x)); 
    foo(reinterpret_cast<T*>(x)); 
} 


int main() 
{ 
    cout << "Hello World" << endl; 
    bar<int>(NULL); 
    return 0; 
} 

Без специализации, это выводит 1,2,3 (в явном виде экземпляра вызова отличается от перегруженного вызова), в то время как по специальности Вы получаете 3,2,3 (явная конкретизация такое же, как неявный вызов).

1

#3 может использоваться для специализирования функций шаблона в одном блоке компиляции без необходимости обновления других единиц компиляции.

Допустим, у нас есть z.cpp, который выглядит следующим образом:

template <class T> void foo (T*) { puts("1"); } 
template <class T> void foo (T) { puts("2"); } 
template <> void foo (int*) { puts("3"); } 
void foo(int*) { puts("4"); } 

int dummy() { 
    foo((int*)NULL);  
    foo<int>((int*)NULL); 
    foo(4);  
    foo((long*)NULL);  
} 

и y.cpp который выглядит следующим образом:

#include <stdio.h> 

template <class T> void foo (T*); 
template <class T> void foo (T); 

int main() { 
    foo((int*)NULL);  
    foo<int>((int*)NULL); 
    foo(4);  
    foo((long*)NULL);  
} 

Первые строки основной будет относиться к #3 и не #4. Это означает, что мы можем специализировать foo для других типов в z.cpp без изменения y.cpp.

С #4, вам нужно будет обновить y.cpp каждый раз, когда вы добавили новую перегрузку.

+0

Я на 99% уверен, что такой код нарушает одно правило определения и, как таковое, является неопределенным поведением. –

+0

@MarkB - Почему? Определения относятся только к 'z .cpp'. – tohava

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