Повторное связывание шаблона или позднее связывание шаблона или как вызвать копирование шаблона в типе
Зачастую концептуально шаблон должен передаваться в качестве аргумента типа, и компилятор жалуется, потому что это недопустимо для C++ - по крайней мере до и включая C++11 (Обновление II: за исключением того, что смотрите в последнем примере определения специализированного шаблона)
Эти понятия часто используются и должны иметь название. Смотрите примеры кода ниже, если не ясно, что я имею в виду.
Моей первой мыслью было, что это также можно назвать передачей неполного типа, но это не правильно. Другой пользователь также заявляет, что у него нет слова для этого и свободно называет это поздним связыванием аргументов шаблона. Я думаю, что его терминология хорошо визуализирует концепцию.
Мой вопрос: как правильно назвать идиому, стоящую за этим, или соответствующие шаблоны?
ОБНОВЛЕНИЕ Kerrek предлагает назвать повторное связывание шаблона. Это имя отображает только несколько результатов Google. Тем не менее, я думаю, что это тоже очень хорошее имя, потому что стандартные распределители называют связанные связанные внутренние классы перепривязанными.
В следующем примере вы можете настроить, если ваша база данных использует map
или же hashmap
внутренне:
#include <map>
struct map_wrapper
{
template<typename K, typename V>
using type = std::map<K,V>;
};
template<typename storage>
struct my_database
{
typename storage::template type<int, int> x;
typename storage::template type<int, double> y;
};
main ()
{
my_database<map_wrapper> db;
db.x[0] = 1;
db.y[0] = 2.0;
}
Или аналогично
#include <map>
#include <boost/mpl/apply.hpp>
template <typename storage>
struct my_database
{
typename boost::mpl::apply<storage, int>::type x;
typename boost::mpl::apply<storage, double>::type y;
};
int main ()
{
my_database< std::map<int, boost::mpl::_1> > db;
db.x[0] = 1;
db.y[0] = 2.0;
}
Обновление II: К моему смущению, я не знал следующего решения, которое просто передает шаблон в качестве аргумента для создания экземпляра шаблона. В этом особом случае допустимо передавать нетип.
#include <map>
#include <unordered_map>
template<template<typename...> class Storage>
struct my_database
{
Storage <long,char> x;
Storage <long,double> y;
};
int main ()
{
my_database< std::map > db1;
db1.x[0] = '1';
db1.y[0] = 2.2;
my_database< std::unordered_map > db2;
db2.x[0] = '1';
db2.y[0] = 2.2;
}
Конечно, приветствуются примеры других способов повторного или позднего связывания аргументов шаблона.
1 ответ
Вы предоставили шаблон, где ожидается тип.
Для чего это стоит, GCC 4.8 дает достойную диагностику:
template <typename> struct Foo;
template <typename> struct Bar;
int main()
{
Foo<Bar> x;
}
Диагностика:
In function ‘int main()’:
error: type/value mismatch at argument 1 in template parameter list for ‘template<class> struct Foo’
Foo<Bar> x;
^
error: expected a type, got ‘Bar’
(GCC стал намного лучше, так как Clang начал выдавать очень хорошие сообщения об ошибках, так что это может быть недавно; я думаю, Clang также выдаст здесь полезную ошибку.)
Относительно вашего обновленного вопроса: Типичный способ сделать адаптер контейнера или, как правило, класс, которому нужен определенный контейнер, который вы хотите настроить, - это передать весь контейнер как параметр шаблона:
template <typename XContainer, typename YContainer>
struct Database
{
XContainer xstorage;
YContainer ystorage;
};
Database<std::map<int, double>, std::unordered_map<std::string, double>> db;
db.xstorage[1] = 2.5;
db.ystorage["foo"] = 4.5;
Это наиболее общее решение, поскольку вы можете использовать любой класс, который соответствует вашим потребностям, не требуя, чтобы этот класс имел какую-либо конкретную структуру.
В качестве альтернативы (которую я не рекомендую), если вы хотите, чтобы все ваши контейнеры были шаблонными специализациями одного и того же шаблона, вы можете передать шаблон непосредственно как параметр шаблона (так называемый параметр шаблона шаблона):
template <template <typename, typename, typename...> class CTmpl,
typename XType,
typename YType>
struct Database
{
CTmpl<XType, double> x;
CTmpl<YType, double> y;
};
Database<std::map, int, std::string> db;
// as above
Это намного более ограничительно: ваша база данных теперь должна использовать не только одну и ту же структуру данных для всего, но и структуру данных также нужно задавать как шаблон с фиксированными первыми двумя параметрами "тип ключа" и "тип отображения".