Может ли конструктор быть шаблонной функцией с

Обновлено: 13.05.2024

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

Определение и использование шаблонов

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

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

В другом месте пользователь может объявить экземпляр шаблона, специализированного для int. Предположим, что get_a() и get_b() являются функциями, возвращающими целое число:

Тем не менее, поскольку это шаблон функции, и компилятор может вывести тип T из аргументов a и b, его можно вызвать так же, как обычная функция:

При обнаружении последней инструкции компилятор создает новую функцию, в которой каждое вхождение T в шаблоне заменяется следующим int :

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

Параметры типа

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

Практические ограничения на количество параметров типа не ограничено. Разделите несколько параметров запятыми:

Ключевое слово class эквивалентно этому контексту typename . Предыдущий пример можно выразить следующим образом:

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

Любой встроенный или определяемый пользователем тип можно использовать в качестве аргумента типа. Например, можно использовать std::vector в стандартной библиотеке для хранения переменных типа int , double std::string, MyClass const MyClass *, MyClass& и т. д. Основное ограничение при использовании шаблонов заключается в том, что аргумент типа должен поддерживать любые операции, применяемые к параметрам типа. Например, если мы вызываем minimum использование MyClass , как показано в следующем примере:

Ошибка компилятора будет создана, так как MyClass не предоставляет перегрузку для оператора.

Нет никаких обязательных требований к тому, что аргументы типа для любого конкретного шаблона принадлежат к одной иерархии объектов, хотя можно определить шаблон, который применяет такое ограничение. Вы можете сочетать объектно-ориентированные методы с шаблонами; Например, можно сохранить производный* в vectorBase. Обратите внимание, что аргументы должны быть указателями

Базовые требования, которые std::vector и другие контейнеры стандартной T библиотеки накладывают на элементы, которые T можно скопировать и сконструировать.

Параметры, не относящиеся к типу

Обратите внимание на синтаксис в объявлении шаблона. Значение size_t передается в качестве аргумента шаблона во время компиляции и должно быть const или выражением constexpr . Используйте его следующим образом:

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

Вычет типов для параметров шаблона, не относящихся к типу

В Visual Studio 2017 и более поздних версиях, а также в /std:c++17 режиме или более поздних версиях компилятор выводит тип аргумента шаблона, не относящееся к типу, объявленному с помощью auto :

Шаблоны в качестве параметров шаблона

Шаблон может быть параметром шаблона. В этом примере MyClass2 имеет два параметра шаблона: параметр typename T и параметр шаблона Arr:

Так как сам параметр Arr не имеет основного текста, его имена параметров не требуются. На самом деле, это ошибка для ссылки на имя типа или имена параметров класса Arr из текста MyClass2 . По этой причине имена параметров типа Arr могут быть опущены, как показано в следующем примере:

Аргументы шаблона по умолчанию

Шаблоны классов и функций могут иметь аргументы по умолчанию. Если у шаблона есть аргумент по умолчанию, его можно оставить неуказаным при его использовании. Например, шаблон std::vector имеет аргумент по умолчанию для распределителя:

В большинстве случаев класс std::allocator по умолчанию является допустимым, поэтому используется вектор следующим образом:

Но при необходимости можно указать пользовательский распределитель следующим образом:

При наличии нескольких аргументов шаблона все аргументы после первого аргумента по умолчанию должны иметь аргументы по умолчанию.

При использовании шаблона, параметры которого используются по умолчанию, используйте пустые угловые скобки:

Специализация шаблонов

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

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

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


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

Механизм шаблонов в языке С++ позволяет решать проблему унификации алгоритма для различных типов: нет необходимости писать различные функции для целочисленных, действительных или пользовательских типов – достаточно составить обобщенный алгоритм, не зависящий от типа данных, основывающийся только на общих свойствах. Например, алгоритм сортировки может работать как с целыми числами, так и с объектами типа «автомобиль».

Существуют шаблоны функций и шаблоны классов.

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

Шаблоны классов -– обобщенное описание пользовательского типа, в котором могут быть параметризованы атрибуты и операции типа. Представляют собой конструкции, по которым могут быть сгенерированы действительные классы путём подстановки вместо параметров конкретных аргументов.

Рассмотрим более подробно шаблоны функций.

Шаблоны функций

Как написать первую шаблонную функцию?

Рассмотрим случай определения минимального элемента из двух. В случае целых и действительных чисел придется написать 2 функции.

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

Что произойдёт в случае компиляции приложения? Обе реализации функции попадут в бинарный код приложения, даже если они не используются (впрочем, сейчас компиляторы очень умные, умеют вырезать неиспользуемый код). А если необходимо добавить функцию, определяющую минимальную из 2 строк (сложно представить без уточнения, что есть минимальная строка)?!

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

  1. берётся реализация функции для какого-то типа;
  2. приписывается заголовок template (или template), что означает, что в алгоритме используется какой-то абстрактный тип Type;
  3. в реализации функции имя типа заменяется на Type.


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

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


Решается эта проблема указанием конкретного типа при вызове функции.

Когда шаблонная функция (не) будет работать?

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

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

Перегрузка шаблона функции

Шаблоны функций также могут перегружаться. Обычно данная перегрузка выполняется при

Частные случаи

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


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

На будущее остаются множественные и целочисленные параметры. Естественным продолжением являются шаблоны классов, основы порождающего программирования, и устройство стандартной библиотеки С++. И куча примеров!

Доброго времени суток читающим.

Вопрос к знатокам стандарта, и, ясное дело, о сабж. Возможно ли такое, ну хотя бы на некоторых компиляторах, или хотя бы в новых версиях стандарта? В Интернетах(ТМ) нашел всего одно древнее упоминание, что вроде нельзя.

P.S. Фабрику параметризованную типом, конечно, сделать можно:

Но, сами понимаете, "если нет разницы, то зачем платить больше?". В смысле, если можно создать конструктор, то зачем городить фабрику?

Здравствуйте, slava_phirsov, Вы писали:

_>Доброго времени суток читающим.

_>Вопрос к знатокам стандарта, и, ясное дело, о сабж. Возможно ли такое, ну хотя бы на некоторых компиляторах, или хотя бы в новых версиях стандарта? В Интернетах(ТМ) нашел всего одно древнее упоминание, что вроде нельзя.

Здравствуйте, Bell, Вы писали:

main.cpp:25: error: `Foo' is not a template

работать должен и первый ваш вариант, а предложенный Bell'ом тоже будет работать — только нужно передавать аргумент типа long, а не явно указывать тип.

Здравствуйте, Lorenzo_LAMAS, Вы писали:

L_L>работать должен и первый ваш вариант, а предложенный Bell'ом тоже будет работать — только нужно передавать аргумент типа long, а не явно указывать тип.

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

Здравствуйте, slava_phirsov, Вы писали:

_>Доброго времени суток читающим.

_>Вопрос к знатокам стандарта, и, ясное дело, о сабж. Возможно ли такое, ну хотя бы на некоторых компиляторах, или хотя бы в новых версиях стандарта? В Интернетах(ТМ) нашел всего одно древнее упоминание, что вроде нельзя.

_>P.S. Фабрику параметризованную типом, конечно, сделать можно:


Можно, но с ограничениями. Поскольку нет возможности указать параметры шаблонного конструктора явно, должна быть обеспечена возможность их автоматического выведения. Вот что сказано в стандарте:

14.2.5/5
[Note: because the explicit template argument list follows the function template name, and because conversion member function templates and constructor member function templates are called without using a function name, there is no way to provide an explicit template argument list for these function templates. ]

Здравствуйте, rg45, Вы писали:


А в новых версиях стандарта сохраняется та же ситуация?

_>main.cpp:25: error: `Foo' is not a template

Здравствуйте, slava_phirsov, Вы писали:

ОК, еще дополнительно объясню, зачем оно надо. Имею желание создать у класса кучку перегруженных конструкторов, принимающих в качестве аргумента целое число, т.е.:

Это можно было бы сделать при помощи шаблонов, если бы можно было объявить шаблон конструктора с явной специализацией. А иначе, какой конструктор будет вызван, если клиентский код сделает вот так:

Литерал "3" для разных компиляторов и разных настроек компилятора, насколько я помню, может быть и short, и long. Кроме того, клиентский код может захотеть вызвать конструктор для char или какого-нибудь compiler-specific типа. ИМХО, спасение тут только одно — задавать тип явно. Конечно, можно употребить static_cast:

Но тоже как-то гружено выглядит.

Здравствуйте, slava_phirsov, Вы писали:

сорри, перепутал, конечно:

_>

Здравствуйте, slava_phirsov, Вы писали:

_>Литерал "3" для разных компиляторов и разных настроек компилятора, насколько я помню, может быть и short, и long.
Да нет, он будет иметь тип int.

Здравствуйте, rg45, Вы писали:

И, вдогонку еще: если будут определены шаблонный конструктор и нешаблонный

то как компилятор определит, кого из них вызвать? При аргументе типа Bar будет однозначно вызываться Foo(Bar b), или как? MinGW32 делает именно так, но ИМХО подход "пока что работает" — не самый лучший. У Страуса по этому поводу четких объяснений не нашел

_>main.cpp:25: error: `Foo' is not a template

[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]

Здравствуйте, slava_phirsov, Вы писали:

[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]

Здравствуйте, Vain, Вы писали:

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

Здравствуйте, Vain, Вы писали:

V>Либо так:

тут возникает другая проблема:

Здравствуйте, slava_phirsov, Вы писали:

_>Здравствуйте, rg45, Вы писали:

_>А в новых версиях стандарта сохраняется та же ситуация?

Да, приведенное примечание присутствует и в черновике C++0x.

Здравствуйте, slava_phirsov, Вы писали:

_>Здравствуйте, rg45, Вы писали:

_>И, вдогонку еще: если будут определены шаблонный конструктор и нешаблонный

_>

_>то как компилятор определит, кого из них вызвать? При аргументе типа Bar будет однозначно вызываться Foo(Bar b), или как? MinGW32 делает именно так, но ИМХО подход "пока что работает" — не самый лучший. У Страуса по этому поводу четких объяснений не нашел

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

Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, Vain, Вы писали:

V>>Либо так:

NB>тут возникает другая проблема:
NB>


Что опять возвращает нас к необходимости явно указывать тип аргумента (а зачем тогда нужен helper-класс Foo::dtype?).

Здравствуйте, night beast, Вы писали:

V>>Либо так:
NB>тут возникает другая проблема:
NB>
Ну она бы возникла и для приведённого автором псевдокода.

[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]

Давайте рассмотрим простой пример. Допустим, у нас есть функция, которая меняет местами значения двух переменных типа int:

Теперь, допустим, у нас в функции main так же есть две переменные типа double, значения которых тоже нужно обменять. Функция для обмена значений двух переменных типа int нам не подойдет. Напишем функцию для double:

И теперь перепишем main:

Для начала, заглянем в википедию и посмотрим, что же такое шаблоны:

Итак, описание шаблона начинается с ключевого слова template за которым в угловых скобках («») следует список параметров шаблона. Далее, собственно идет объявление шаблонной сущности (например функция или класс), т. е. имеет вид:

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

typename в угловых скобках означает, что параметром шаблона будет тип данных. T — имя параметра шаблона. Вместо typename здесь можно использовать слово class: template В данном контексте ключевые слова typename и class эквивалентны (лично мне больше нравится typename, а кому-то class). Далее, в тексте шаблона везде, где мы используем тип T, вместо T будет проставляться необходимый нам тип.

теперь давайте напишем функцию main:

Как видите, после имени функции в угловых скобках мы указываем тип, который нам необходим, он то и будет типом T. Шаблон — это лишь макет, по которому компилятор самостоятельно будет генерировать код. При виде такой конструкции: my_swap компилятор сам создаст функцию my_swap с необходимым типом. Это называется инстанцирование шаблона. То есть при виде my_swap компилятор создаст функцию my_swap в которой T поменяет на int, а при виде my_swap будет создана функция с типом double. Если где-то дальше компилятор опять встретит my_swap , то он ничего генерировать не будет, т.к. код данной функции уже есть(шаблон с данным параметром уже инстанцирован).

Таким образом, если мы инстанцируем этот шаблон три раза с разными типами, то компилятор создаст три разные функции

Вывод типа шаблона исходя из параметров функции

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

рассмотрим вызов функции без указания типа:

Наша шаблонная функция принимает параметры типа T&, основываясь на шаблоне, компилятор видит, что Вы передаете в функцию аргументы типа int, поэтому может самостоятельно определить, что в данном месте имеется ввиду функция my_swap с типом int. Это deducing template arguments. Теперь давайте напишем пример посложнее. Например, программу сортировки массива(будем использовать сортировку «пузырьком»). Естественно, что алгоритм сортировки один и тот же, а вот типы элементов в массиве будут отличаться. Для обменивания значений будем использовать нашу шаблонную функцию my_swap. Приступим:

Source arrays: 10 5 7 3 4 7.62 5.56 38 56 9 Sorted arrays: 3 4 5 7 10 5.56 7.62 9 38 56

Как видите, компилятор сам генерирует out_array для необходимого типа. Так же он сам генерирует функцию bubbleSort. А в bubbleSort у нас применяется шаблонная функция my_swap, компилятор сгенерирует и её код автоматически. Удобно, не правда ли?

Введение в шаблонные классы

Шаблонными могут быть не только функции. Рассмотрим шаблонные классы. Начнем с простого примера. Мы добавим в наш предыдущий код функцию, которая будет искать максимум и минимум в массиве. При создании функции «упираемся» в проблему — как вернуть два указателя? Можно передать их в функцию в качестве параметров, а можно вернуть объект, который будет содержать в себе два указателя. Первый вариант при большом кол-ве возвращаемых значений приведет к заваливанию функции параметрами, поэтому я предлагаю сделать структуру:

А какого же типа будут указатели? Можно сделать их void*, но тогда придется постоянно кастовать их к нужному типу, и код станет похож на «Доширак». А что, если сделать эту структуру шаблонной? Попробуем:

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

Компилятор не будет автоматически определять типы для шаблона класса, поэтому необходимо их указывать самостоятельно.

Теперь давайте напишем код шаблонной функции для поиска максимума и минимума:

Теперь мы можем вызывать данную функцию:

Для классов мы должны явно указывать параметры шаблона. В стандарте C++11, устаревшее ключевое слово auto поменяло свое значение и теперь служит для автоматического вывода типа в зависимости от типа инициализатора, поэтому мы можем написать так:

Предлагаю написать еще одну функцию, которая будет выводить объект my_pointer_pair в стандартный поток вывода:

Arrays: 10 5 7 3 4 7.62 5.56 38 56 9 min = 3 max = 10 min = 5.56 max = 56

Шаблоны и STL

В комплекте с компилятором Вам предоставляется стандартная библиотека шаблонов (Standart Template Library). Она содержит множество шаблонных функций и классов. Например, класс двусвязного списка(list), класс «пара» (pair), функция обмена двух переменных(swap), функции сортировок, динамически расширяемый массив(vector) и т.д. Всё это — шаблоны и Вы можете их использовать. Для небольшого примера возьмем std::vector:

Заметьте, когда писали std::vector, авторы понятия не имели, элементы какого типа Вы будете хранить.

Шаблоны это слишком большой и мощный инструмент и описать всё в одной статье не представляется возможным. Это было лишь небольшое введение в мир шаблонов. Углубляясь в шаблоны, Вы поразитесь тому, какой мощный это инструмент и какие возможности он предоставляет.

P.S. высказывайте мнение о статье, критику, дополнения/исправления и интересующие вопросы в комментариях.

P.P.S. Просьба вопросы «консоль закрывается, что делать?», «русский язык не показывает. Что делать?», «как работает сортировка?», «что такое size_t», «что такое std::» и им подобные задавать либо в гугл, либо искать на данном сайте в других статьях. Не нужно захламлять комментарии этой чепухой. Если Вы этого не знаете, то может лучше сначала подтянуть свои знания?

Функции-члены класса тоже могут быть шаблонными. Например, у нас имеется класс Math со статической функцией abs , которая вычисляет абсолютное значение числа:

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

Как видите, всё просто. Шаблонными могут быть не только статические функции. Но необходимо учесть, что виртуальные функции не могут быть шаблонными:

Также шаблонными могут быть операторы и конструкторы:

my_class имеет один существенный недостаток — это объекты абсолютно разных типов. Если мы попробуем сравнить объекты obj1 и obj2 , то получим ошибку времени компиляции, ведь как эти объекты сравнивать компилятору неизвестно.

Конечно, можно сравнить результаты вызова функции getX , но это всё равно не решает всех проблем — нельзя присвоить один объект другому или сконструировать один объект из другого(если их типы разные).

Чтобы выйти из ситуации, мы определим шаблонные версии конструкторов и операторов.

Как видите, теперь преобразования возможны, конечно, если возможны преобразования между объектами m_x в самих классах, т.е. написать my_class obj4(obj3); в данном случае не получится, т.к. нет соответствующего преобразования из типа double в тип std::string .

Как вы могли заметить, я убрал из класса нешаблонные конструктор и оператор сравнения, оставив только их шаблонные версии. Но можно было их и оставить, чтобы лучше организовать работу с одинаковыми типами (например, избежать накладных расходов на преобразования).

Из данного примера также видно, что у шаблонного класса могут быть шаблонные функции-члены. Хочется еще отметить, что у шаблонного класса могут быть виртуальные функции, но виртуальные функции не могут быть шаблонными.

Аргументы шаблона не-типы.

Аргументами шаблона могут быть не только типы. Рассмотрим небольшой пример

После выполнения получим значения 7 и 5. Эти параметры задаются на этапе компиляции и не совсем очевидно для чего это может быть нужно. Вскоре мы рассмотрим применение таких аргументов, посмотрите на шаблон класса std::bitset , в котором аргумент не-тип задает размер множества:

Как известно, при разных аргументах шаблона компилятор создаст разные типы. То есть my_class и my_class — это разные типы. Так же и с параметрами не-типами, т.е. my_type и my_type — это тоже разные типы. Это свойство нам тоже понадобится в дальнейшем. Но на время отвлечемся от этого.

Для начала определимся, что же можно использовать в качестве аргумента не-типа. Для этого обратимся к стандарту.

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,
  • std::nullptr_t.

Ух ты, какой выбор. Но увы, есть и другие ограничения.

  • an integral constant expression (including a constant expression of literal class type that can be used as an integral constant expression as described in 5.19); or
  • the name of a non-type template-parameter; or
  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
  • a constant expression that evaluates to a null pointer value (4.10); or
  • a constant expression that evaluates to a null member pointer value (4.11); or
  • a pointer to member expressed as described in 5.3.1.

Это еще не всё, но для начала этого достаточно. Попробуем передать в параметр шаблона ссылку на объект:

Для начала этой информации нам хватит.

Шаблонные параметры шаблона.

Кратко рассмотрим передачу шаблона в качестве аргумента шаблона. Создадим функцию, которая принимает в качестве аргумента бинарный предикат и ссылки на две переменные. В случае если предикат возвращает true, делаем swap переданных переменных:

А теперь попробуем передать предикат std::less как параметр шаблона. Для этого необходимо изменить функцию foo . Шаблонные аргументы шаблона (template template arguments) определяются как

В данном случае использование ключевого слова class — принципиально и typename его не заменит.

template < typename >class BinPred — вот он, наш шаблонный аргумент. Как видим, у шаблонного аргумента один аргумент, его имя не имеет значения, поэтому отсутствует. BinPred()(obj1,obj2) — здесь создаем объект класса BinPred() и вызываем у него функцию operator() с двумя параметрами.

Читайте также: