C ++ 11"后期绑定"模板参数

时间:2013-02-22 17:11:11

标签: c++ templates 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集实现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 ++!

所以我想到了解决这个问题的方法。我能想到的唯一解决方案是为我想要使用的所有数据结构编写“binder类”,如下所示:

struct btree_set_binder{

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

现在我可以使用set binder

定义我的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
};

现在,用户必须只提供所需的binder类,Containor将使用正确的参数对其进行实例化。所以这些绑定器类对我来说没问题,但是为所有容器编写绑定器类非常麻烦。那么有没有更好或更简单的方法在C ++ 11中“迟到”绑定模板参数,即在另一个模板中检索原始模板作为参数。

3 个答案:

答案 0 :(得分: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;

    // ...
};

我已将您的&#34; Containor&#34;重新命名为因为这种结构通常被称为&#34;适配器&#34;以及#34; MyAdaptor&#34;类。

<强>用法:

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模板作为练习。

答案 1 :(得分:2)

  

所以我想要的是客户端代码只交付原始代码的方式   模板和Containor内部使用正确的方法进行实例化   参数,

所以你需要的是一个模板模板参数:

// 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; 
    // ...
};

答案 2 :(得分:1)

您应该可以使用template template参数执行此操作,如:

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

注意:您需要可变参数typename...,因为std::set需要三个模板参数(第三个是分配器),而其他容器可能没有。