В библиотечном адаптере нужно принять шаблон контейнера без создания его объекта. Какой вид параметра шаблона для этого используют?
Для передачи самого шаблона используют шаблонный параметр-шаблон — template template parameter. Он принимает не конкретный тип контейнера, а шаблон, из которого позднее можно получить тип, подставив параметры.
Компилятор проверяет совместимость переданного шаблона с объявленной формой параметра: количество, виды параметров, параметры по умолчанию и шаблонные пакеты. Наличие нужных методов контейнера этим механизмом не проверяется.
Обычный параметр типа позволяет передать уже сформированный тип, например конкретный тип контейнера. Однако библиотеке часто требуется выбрать целое семейство типов: вектор, список или пользовательский контейнер, а затем самостоятельно подставить в него тип элемента и дополнительные параметры.
Шаблонный параметр-шаблон решает эту задачу на уровне типов. Он позволяет строить обобщённые компоненты, которые управляют созданием конкретного типа из переданного шаблона.
Если принять контейнер как обычный параметр типа, вызывающий код должен заранее указать полностью сформированный тип. Такой подход неудобен, когда адаптер сам должен подставлять тип элемента, аллокатор или другие параметры.
Если форма шаблонного параметра описана слишком узко, подходящий контейнер может не пройти проверку совместимости ещё до инстанцирования. Если она описана слишком широко, ошибки интерфейса проявятся позднее — при попытке использовать отсутствующий метод или неподходящий тип.
Шаблонный параметр-шаблон объявляют конструкцией, в которой после ключевого слова template указывают параметры будущего шаблона. Внешний шаблон получает не объект и не тип, а имя шаблона-кандидата.
У Buffer параметр Container должен быть шаблоном с двумя параметрами типа. std::vector подходит: его параметры включают тип элемента и тип аллокатора, поэтому внутри Buffer можно сформировать std::vector<int, std::allocator<int>>.
Проверяется именно форма шаблона, а не его поведение. Компилятор не убеждается заранее, что контейнер имеет методы push_back, size или совместимый итератор. Такие требования проверяются только при инстанцировании кода, который их использует.
При сопоставлении учитываются виды параметров: параметр типа должен соответствовать параметру типа, нетиповой параметр — совместимому нетиповому параметру, а шаблонный параметр — вложенной шаблонной форме. Параметры по умолчанию и пакеты параметров влияют на допустимость сопоставления.
Правила сопоставления стали гибче в C++17, поэтому некоторые шаблоны с дополнительными параметрами по умолчанию могут подходить к более короткой форме параметра-шаблона. Для переносимой библиотеки всё равно следует проверять целевой стандарт и не полагаться на расширения компилятора.
Компромисс состоит в том, что шаблонный параметр-шаблон хорошо выражает зависимость от семейства типов, но плохо выражает поведенческий контракт. Если контейнеры имеют сильно различающиеся параметры или интерфейсы, иногда лучше принять готовый тип, использовать концепт для проверки сформированного типа или применить отдельный адаптер.
Нужно реализовать буфер, который хранит элементы выбранного контейнера и сам подставляет тип элемента и аллокатор. Рассматривались три варианта.
Первый вариант — принять готовый тип контейнера как обычный параметр типа. Он наиболее гибок и позволяет использовать любой тип, но внешний код должен заранее сформировать контейнер с правильными параметрами. Адаптер теряет контроль над созданием внутреннего типа.
Второй вариант — принять шаблонный параметр-шаблон с формой, соответствующей контейнерам из библиотеки. Это позволяет передать std::vector или std::list, а тип элемента и аллокатор подставить внутри. Минус — шаблоны с несовместимой параметрической формой не подойдут, даже если их фактический интерфейс аналогичен.
Третий вариант — принять произвольный тип и проверять его возможности концептом. Он лучше описывает поведенческий контракт, но не решает задачу передачи семейства шаблонов: конкретный тип всё равно должен быть создан заранее.
Для описанной задачи выбирают шаблонный параметр-шаблон. Он точно отражает требование передать фабрику типов, а ограничения поведения при необходимости дополняют отдельной проверкой после формирования конкретного контейнера.
Нет. Он проверяет совместимость параметрической формы переданного шаблона. Например, шаблон может пройти сопоставление, но затем не скомпилироваться при обращении к push_back, если такого метода нет. Для проверки поведения нужны концепты, requires или другая форма ограничения.
Обычный параметр типа представляет уже конкретный тип, например std::vector<int>. Шаблонный параметр-шаблон представляет шаблон, из которого можно получить разные конкретные типы: std::vector<int>, std::vector<double> и так далее. Поэтому внутри внешнего шаблона его можно повторно инстанцировать с выбранными аргументами.
Да, алиас-шаблон может быть аргументом шаблонного параметра-шаблона, если его параметрическая форма совместима с ожидаемой. Однако алиас не создаёт новый тип: он лишь обозначает результат другой шаблонной конструкции. Поэтому проверяется возможность корректно подставить параметры в алиас, а не наличие отдельного класса с самостоятельной реализацией.