Повторное связывание шаблона или позднее связывание шаблона или как вызвать копирование шаблона в типе

Зачастую концептуально шаблон должен передаваться в качестве аргумента типа, и компилятор жалуется, потому что это недопустимо для 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

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

Другие вопросы по тегам