C++11 "позднее связывание" аргументов шаблона

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

Предположим, я работаю над контейнерной (или аналогичной) структурой данных Containor для некоторого типа значения V что нужно сравнить эти значения с компаратором, поэтому мой первый шаблон выглядит следующим образом

template<typename Val, typename Comp = std::less<Val>>
struct Containor{};

Теперь мой Containor структура использует другой контейнер внутри. Какой контейнер должен использоваться, также должен быть конфигурируем с помощью аргументов шаблона, скажем, по умолчанию std::set, Так что моя следующая версия Containor выглядит так:

template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set<Val,Comp>>
struct Containor{};

и вот тут код начинает пахнуть ИМХО. Пока пользователь удовлетворен реализацией внутреннего контейнера по умолчанию, все в порядке. Однако предположим, что он хочет использовать новую реализацию Google btree set btree::btree_set вместо std::set, Затем он должен создать экземпляр шаблона следующим образом:

typedef Containor<int,std::less<int>,btree::btree_set<int,std::less<int>> MyContainor;
                                                     ^^^^^^^^^^^^^^^^^^^

Я подчеркнул ту часть, в которой лежит моя проблема. КОД КЛИЕНТА должен создать экземпляр btree_set с правильными параметрами. Это честно отстой, потому что Containor Классу всегда нужен набор точно такого же типа и компаратора, как и его первые два аргумента шаблона. Клиент может - случайно - вставить сюда другие типы! Кроме того, на клиенте лежит бремя выбора правильных параметров. Это может быть легко в этом случае, но трудно, если внутренний контейнер должен, например, представлять собой набор пар типа значения и некоторого другого типа. Тогда клиенту еще труднее получить правильные параметры типа внутреннего набора.

Так что я хочу, чтобы клиентский код передавал только необработанный шаблон и Containor внутренне инстанцирует это с правильными аргументами, то есть что-то вроде этого:

template<typename Val, typename Comp = std::less<Val>, typename Cont = std::set >
struct Containor{
    typedef Cont<Val,Comp> innerSet; 
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ container instanciates the inner containor
};

typedef Containor<int,std::less<int>,btree::btree_set> MyContainor;
//                                   ^^^^^^^^^^^^^^^^
//                         client only hands in  raw template

Конечно, это не действительный C++!

Вот я и подумал о способах решения этой проблемы. Единственное решение, о котором я мог подумать, - это написание "связующих классов" для всех структур данных, которые я хочу использовать, например:

struct btree_set_binder{

    template<typename V, typename C = std::less<V>>
    struct bind{
        typedef btree::btree_set<V,C> type;
    }
};

Теперь я могу определить мой Containor с набором связующего

template<typename Val, typename Comp = std::less<Val>, typename ContBinder = btree_set_binder >
struct Containor{
    typedef btree_set_binder::bind<Val,Comp>::type innerSet; 
//          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ works like a charm
};

Теперь пользователь должен предоставить только нужный класс связующего и Containor будет подтверждать это с правильными аргументами. Так что эти классы связывателей были бы хороши для меня, но довольно сложно писать классы связывателей для всех контейнеров. Так есть ли лучший или более простой способ привязать аргументы шаблона "поздно" в C++11, то есть внутри другого шаблона, который извлекает необработанный шаблон в качестве параметра.

3 ответа

Решение

Может быть, сделать свою собственную черту компаратора.

// Comparator trait primary template

template <typename T> stuct MyComparator
{
    typedef typename T::key_compare type;
};

// Comparator trait non-standard usage example

template <typename U, typename V, int N>
struct MyComparator<WeirdContainer<U, V, N>>
{
    typedef std::greater<V> type;
};

template <typename T, typename Cont = std::set<T>>
struct MyAdaptor
{
    typedef typename MyComparator<Cont>::type comparator_type;
    typedef T value_type;

    // ...
};

Я переименовал ваш "Контейнер" в "MyAdaptor", так как этот вид конструкции обычно называется классом "адаптера".

Использование:

MyAdaptor<int> a;    // uses std::set<int> and std::less<int>

MyAdaptor<double, WeirdContainer<bool, double, 27>> b;

Обновление: в свете обсуждения вы можете даже полностью удалить аргумент внешнего типа:

template <typename Cont> struct MyBetterAdaptor
{
    typedef MyAdaptorTraits<Cont>::value_type value_type;
    typedef MyAdaptorTraits<Cont>::pred_type pred_type;

    // ...
};

Быть использованным так:

MyBetterAdaptor<std::set<int>> c; // value type "int", predicate "std::less<int>"

Написание MyAdaptorTraits Шаблон оставлен в качестве упражнения.

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

Так что, очевидно, вам нужен параметр шаблона шаблона:

// std::set has three template parameters, 
// we only want to expose two of them ...
template <typename V, typename C>
using set_with_defalloc = std::set<V,C>;

template<
    typename Val,
    typename Comp = std::less<Val>, 
    template <typename V, typename C> class Cont = set_with_defalloc>
struct Containor{
    typedef Cont<Val,Comp> innerSet; 
    // ...
};

Вы должны быть в состоянии сделать это с templatetemplate параметры, как в:

template<typename Val, typename Comp = std::less<Val>, template <typename...> class ContBinder = std::set>
    struct Containor {
        typedef ContBinder<Val, Comp> innerSet;
        // ...
    };

Примечание: вам нужен вариант typename... так как std::set принимает три параметра шаблона (третий является распределителем), а другие контейнеры - нет.

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