Частичная специализация шаблона
Эта статья нуждается в дополнительных цитатах для проверки . ( декабрь 2014 г. ) |
Частичная специализация шаблона — это особая форма специализации шаблона класса . Обычно используемый применительно к C++ языку программирования , он позволяет программисту специализировать только некоторые аргументы шаблона класса, в отличие от явной полной специализации, при которой предоставляются все аргументы шаблона.
Шаблоны и специализация
[ редактировать ]Шаблоны классов на самом деле являются метаклассами: это частичные абстрактные типы данных, которые предоставляют компилятору инструкции о том, как создавать классы с соответствующими элементами данных. Например, стандартные контейнеры C++ представляют собой шаблоны классов. Когда программист использует вектор, он создает его экземпляр с определенным типом данных, например, int, string или double. Каждый тип вектора приводит к созданию отдельного класса в объектном коде компилятора, каждый из которых работает со своим типом данных. Этот процесс называется мономорфизацией дженериков.
Если известно, что шаблон класса будет довольно часто использоваться с определенным типом данных и этот тип данных допускает некоторую оптимизацию (например, смещение битов при использовании целых чисел вместо умножения или деления на 2), можно ввести специализированный шаблон класса с некоторыми из предустановленных параметров шаблона. Когда компилятор видит такой шаблон класса, созданный в коде, он обычно выбирает наиболее специализированное определение шаблона, соответствующее созданию экземпляра. Таким образом, явная полная специализация (та, где указаны все аргументы шаблона) будет предпочтительнее частичной специализации, если все аргументы шаблона совпадают.
Частичная специализация
[ редактировать ]Шаблоны могут иметь более одного типа параметров. Некоторые старые компиляторы позволяют специализировать только все параметры шаблона или ни одного из них. Компиляторы, поддерживающие частичную специализацию, позволяют программисту специализировать некоторые параметры, оставляя другие универсальными.
Пример
[ редактировать ]Предположим, существует KeyValuePair
класс с двумя параметрами шаблона, как показано ниже.
template <typename Key, typename Value>
class KeyValuePair {};
Ниже приведен пример класса, который определяет явную полную специализацию шаблона KeyValuePair
путем объединения целых чисел со строками. Тип класса сохраняет то же имя, что и исходная версия.
template <>
class KeyValuePair<int, std::string> {};
Следующий пример — частичная специализация KeyValuePair
с тем же именем, что и исходная версия, и одним специализированным параметром шаблона.
template <typename Key>
class KeyValuePair<Key, std::string> {};
Следующий пример класса KeyStringPair
является производным от оригинала KeyValuePair
с новым именем и определяет частичную специализацию шаблона. В отличие от явной специализации, описанной выше, только Value параметр шаблона суперкласса является специализированным, а параметр шаблона Key остается универсальным.
template <typename Key>
class KeyStringPair : public KeyValuePair<Key, std::string> {};
Не имеет значения, какие параметры шаблона являются специализированными, а какие остаются общими. Например, следующее также является допустимым примером частичной специализации исходного KeyValuePair
сорт.
template <typename Value>
class IntegerValuePair : public KeyValuePair<int, Value> {};
Предостережения
[ редактировать ]Шаблоны C++ не ограничиваются классами — их также можно использовать для определения шаблонов функций . Хотя шаблоны функций могут быть полностью специализированными, они не могут быть частично специализированными, независимо от того, являются ли они шаблонами функций-членов или шаблонами функций, не являющихся членами. Это может быть полезно для авторов компиляторов, но влияет на гибкость и детализацию того, что могут делать разработчики. [1] Но шаблоны функций могут быть перегружены , что дает почти тот же эффект, что и частичная специализация шаблона функции. [2] Следующие примеры приведены для иллюстрации этих положений.
// legal: base function template
template <typename ReturnType, typename ArgumentType>
ReturnType Foo(ArgumentType arg);
// legal: explicit/full function template specialization
template <>
std::string Foo<std::string, char>(char arg) { return "Full"; }
// illegal: partial function template specialization of the return type
// function template partial specialization is not allowed
// template <typename ArgumentType>
// void Foo<void, ArgumentType>(ArgumentType arg);
// legal: overloads the base template for a pointer argument type
template <typename ReturnType, typename ArgumentType>
ReturnType Foo(ArgumentType *argPtr) { return "PtrOverload"; }
// legal: base function name reused. Not considered an overload. ill-formed: non-overloadable declaration (see below)
template <typename ArgumentType>
std::string Foo(ArgumentType arg) { return "Return1"; }
// legal: base function name reused. Not considered an overload. ill-formed: non-overloadable declaration (see below)
template <typename ReturnType>
ReturnType Foo(char arg) { return "Return2"; }
Обратите внимание, что в приведенном выше примере, хотя последние два определения функции Foo
являются законными C++, они считаются неправильно сформированными в соответствии со стандартом, поскольку являются неперегружаемыми объявлениями. [3] Это связано с тем, что определение перегрузки функции учитывает только имя функции, список типов параметров и охватывающее пространство имен (если таковое имеется). Он не учитывает тип возвращаемого значения. [4] Однако эти функции по-прежнему можно вызывать, явно указав компилятору сигнатуру, как показано в следующей программе.
// note: to be compiled in conjunction with the definitions of Foo above
int main(int argc, char *argv[])
{
char c = 'c';
std::string r0, r1, r2, r3;
// let the compiler resolve the call
r0 = Foo(c);
// explicitly specify which function to call
r1 = Foo<std::string>(c);
r2 = Foo<std::string, char>(c);
r3 = Foo<std::string, char>(&c);
// generate output
std::cout << r0 << " " << r1 << " " << r2 << " " << r3 << std::endl;
return 0;
}
//expected output:
Return1 Return2 Full PtrOverload
Ссылки
[ редактировать ]- ^ Александреску, Андрей (1 февраля 2001 г.). Современный дизайн на C++ . Эддисон Уэсли. стр. 23. ISBN 0-201-70431-5 .
- ^ Саттер, Херб (июль 2001 г.). «Почему бы не специализироваться на шаблонах функций?» . Журнал пользователей C/C++ . 19 (7) . Проверено 7 декабря 2014 г.
- ^ «ISO/IEC JTC1 SC22 WG21 N 3690: Языки программирования — C++» (PDF) . ИСО. 15 мая 2013 г. с. 294 . Проверено 16 октября 2016 г.
13.1. Перегружаемые объявления [over.load] Не все объявления функций могут быть перегружены. Здесь указаны те, которые нельзя перегрузить. Программа считается неправильной, если она содержит два таких неперегружаемых объявления в одной области действия.
- ^ «ISO/IEC JTC1 SC22 WG21 N 3690: Языки программирования — C++» (PDF) . ИСО. 15 мая 2013 г. стр. 294–296 . Проверено 16 октября 2016 г.
13.1 Перегружаемые объявления [over.load]