【问题标题】:C++11 "late binding" of template arguments模板参数的 C++11“后期绑定”
【发布时间】:2013-02-22 17:11:11
【问题描述】:

请不要把我的“后期绑定”弄错了,我不是指运行时通常的后期绑定,我指的是别的东西,找不到更好的词来形容它:

考虑我正在为一些值类型V 处理容器(或类似)数据结构Containor,需要将这些值与比较器进行比较,所以我的第一个模板看起来像这样

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++!

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

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 将使用正确的参数实例化它。所以这些 binder 类对我来说没问题,但是为所有容器编写 binder 类相当麻烦。那么有没有更好或更简单的方法来在 C++11 中“晚”绑定模板参数,即在另一个模板中,将原始模板作为参数检索。

【问题讨论】:

  • 您的内部容器是否总是需要谓词?然后将其提取出来。
  • 我猜你需要的是模板模板参数
  • @Kerrek SB:我不明白你的意思。你能详细说明一下吗?
  • @gexicide: 好吧,使用Cont::key_compare 作为比较器......或者如果你想更灵活,做一个特征。事实上,我会把它作为答案发布。
  • @gexicide:模板模板存在,但您通常应该避免在像您这样的情况下使用它们,因为您永远无法判断客户端模板的实际外观。模板最好公开自己的参数并使用特征。

标签: c++ templates c++11


【解决方案1】:

也许可以制作自己的比较器特征。

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

【讨论】:

  • 仍然,如何在不指定模板参数的情况下将 MyAdaptor 与另一个 Cont 实例化?
  • @Edit:但这并不能解决我的问题。同样,用户必须将所有模板参数提供给WeirdContainer。实例化MyAdaptor&lt;double, WeirdContainer&lt;bool, double, 27&gt;&gt; 时。这些论点正是我想要摆脱的。
  • @gexicide:您将不得不重复值类型,但这是可以支付的价格,因为它是您可以获得的最通用的设计。标准库的构建方式相同,而且是有原因的。
  • @gexicide:嗯,我们确实避免了重复指定谓词,对吗?这是一个很大的进步。我认为这种设计完全可以接受。再次与标准库进行比较——即使他们认为这是一个很好的库设计。
  • 但是请看我的帖子。如果内部容器可能不是值类型,而是像std::tuple&lt;V,V,bool&gt; 这样的非常复杂的类型怎么办。你想把正确处理这种类型的负担转移给用户吗?
【解决方案2】:

所以我想要的是一种客户端代码只提交原始代码的方式 模板和容器在内部用正确的实例化它 论据,

那么你需要的显然是一个模板模板参数:

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

【讨论】:

    【解决方案3】:

    您应该可以使用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 采用三个模板参数(第三个是分配器),而其他容器可能没有。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 1970-01-01
      相关资源
      最近更新 更多