Я прочитал Краткую информацию C++, и она говорит, что специализация шаблона функции является усовершенствованной темой, но я полностью потерян. Кто-либо может предложить пример, почему специализация шаблона функции важна и необходима?
Почему шаблоны функций не поддерживают частичную специализацию, в то время как шаблоны классов делают? Какова базовая логика?
По сути, идея состоит в том, что вы можете писать шаблоны, которые ведут себя обычным образом для общего случая, но могут обрабатывать особые случаи. Одним из примеров использования специализации является std :: vector
. std :: vector
- это специализация, которая упаковывает элементы bool
таким образом, что они используют только один бит на элемент, а не один байт. std :: vector
работает как обычный динамический массив для всех других типов.
Более сложное использование специализации - метапрограммирование. Например, вот пример (из Википедии) того, как использовать специализацию шаблонов для вычисления факториалов во время компиляции.
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
Ваш На вопрос, почему функции не поддерживают частичную специализацию, можно ответить здесь . В приведенном ниже коде показано, как реализовать различные специализации.
template<typename T>
bool Less(T a, T b)
{
cout << "version 1 ";
return a < b;
}
// Function templates can't be partially specialized they can overload instead.
template<typename T>
bool Less(T* a, T* b)
{
cout << "version 2 ";
return *a < *b;
}
template<>
bool Less<>(const char* lhs, const char* rhs)
{
cout << "version 3 ";
return strcmp(lhs, rhs) < 0;
}
int a = 5, b = 6;
cout << Less<int>(a, b) << endl;
cout << Less<int>(&a, &b) << endl;
cout << Less("abc", "def") << endl;
Я не могу придумать примера, и я пытался почти с тех пор, как вы спросили. Как указал Джаганнатх , давний совет не специализировал функции, а вместо этого перегружать их или использовать класс черт (который может быть специализированным, даже частично специализированный).
Например, если вам нужно поменять местами два элемента, то лучше полагаться на перегрузки (более предсказуемые и более расширяемые):
template<class T>
void f() {
T a, b;
using std::swap; // brings std::swap into scope as "fallback"
swap(a, b); // unqualified call (no "std::") so ADL kicks in
// also look at boost::swap
}
И как вы пишете своп для своих типов:
// the cleanest way to do it for a class template:
template<class T>
struct Ex1 {
friend void swap(Ex1& a, Ex1& b) { /* do stuff */ }
};
// you can certainly place it outside of the class instead---but in the
// same namespace as the class---if you have some coding convention
// against friends (which is common, but misguided, IMHO):
struct Ex2 {};
void swap(Ex2& a, Ex2& b) { /* do stuff */ }
Оба из них позволяют Зависимый от аргумента поиск (ADL).
Другие функции, такие как stringify / str или repr (представление), могут аналогичным образом не являться членами и использовать преимущества ADL посредством перегрузки:
struct Ex3 {
friend std::string repr(Ex3 const&) { return "<Ex3 obj>"; }
};
std::string repr(bool b) { return b ? "true" : "false"; }
// possible fallback:
template<class T>
std::string repr(T const& v) {
std::ostringstream out;
out << v;
return out.str();
}
// but in this particular case, I'd remove the fallback and document that
// repr() must be overloaded appropriately before it can be used with a
// particular type; for other operations a default fallback makes sense
Чтобы взглянуть на это с другой стороны, было бы неплохо, если бы шаблоны функций могли служат реестром для конкретных реализаций, но из-за ограничений (в текущем C ++, не уверен, что именно здесь предлагает C ++ 0x) они не работают так же хорошо, как перегрузка или шаблоны классов для этого реестра цель.
Есть одно использование, которое удобно, но не важно : легко определять определенные специализации в отдельной библиотеке, возможно, в общей библиотеке (.so или .dll). Это удобно, потому что требует минимальных изменений в универсальном шаблоне, но не важно, потому что мне это кажется редким (в дикой природе и, конечно, редко в моем опыте), и разработчики все еще могут использовать либо перегрузку, либо переадресацию в полностью специализированный класс. неспециализированный метод шаблона.
Они расположены по адресу/System/Library/Framework/OpenGL.framework/Headers. Чтобы включить их, просто используйте:
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
#include <GLUT/glut.h>
и т.д. Убедитесь в наличии связи с соответствующими рамками, например,
cc <your_file.c> -framework GLUT -framework OpenGL
для OpenGL и GLUT
-121--1607229-Дополнительные предметы для добавления в список, без особого порядка:
Для иллюстрации важности специализации шаблонов функций рассмотрим функцию std:: swap
. По умолчанию std:: swap (x, y)
по существу делает:
T temp = x;
x = y;
y = temp;
, но это может быть неэффективно, поскольку предполагает создание дополнительной копии x
и может выполнять дополнительное копирование в присвоениях. Это особенно плохо, если x
велик (например, если это std:: vector
с множеством элементов). Кроме того, каждая из вышеперечисленных строк может завершиться сбоем и вызвать исключения, потенциально оставляя x
и y
в плохом, несогласованном состоянии.
Для решения этой проблемы многие классы предоставляют собственные методы swap
(включая std:: vector
)вместо этого следует поменять местами указатели на их внутренние данные. Это более эффективно и может быть гарантировано никогда не потерпеть неудачу.
Но теперь у вас есть случай, когда вы можете использовать std:: swap (x, y)
для некоторых типов, но вам нужно вызвать x.swap (y)
для других типов. Это сбивает с толку, и это плохо для шаблонов, поскольку они не смогут поменять местами два объекта общим, согласованным способом.
Но std:: swap
может быть специализированным для вызова x.swap (y)
при вызове определенных типов. Это означает, что вы можете использовать std:: swap
везде и (надеюсь) ожидать, что он будет хорошо себя вести.