【问题标题】:template rebinding vs. late template binding or how to call piggy backing a template in a type模板重新绑定与后期模板绑定或如何在类型中调用捎带模板
【发布时间】:2013-11-13 08:31:58
【问题描述】:

通常从概念上讲,模板必须作为类型参数传递,编译器会抱怨,因为这不是合法的c++ - 至少在c++11(包括)之前(更新二:除了看最后一个非专用模板定义的例子)

这些概念经常被使用并且应该有一个名字。如果不清楚我的意思,请参阅下面的代码示例。

我的第一个想法是这也可以称为传递不完整的类型,但这是不正确的。另一位用户也表示他对此一无所知,并随意将其命名为late binding of template arguments。我认为他的术语很好地形象化了这个概念。

我的问题是你如何正确地调用这背后的成语或涉及的模板?

UPDATE Kerrek 建议将成语命名为template rebinding。此名称仅呈现少数 google 搜索结果。但是我认为这也是一个非常好的名字,因为标准分配器将其相关的包装内部类称为 rebind

在以下示例中,您可以配置您的数据库是否在内部使用maphashmap

#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;
}

更新二:令我尴尬的是,我不知道以下解决方案,它只是将模板作为参数传递给模板实例化。在这种特殊情况下,传递非类型是合法的。

#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;
}

当然欢迎重新绑定延迟绑定模板参数的其他方法示例。

【问题讨论】:

  • @Kerrek,我不确定网络礼仪,但如果我根据您的建议改进我的问题并因此使您的 cmets 无效,您是否欢迎?无论如何,我邀请任何人编辑和改进我的问题。
  • 这很好。评论是短暂的,如果原始评论作者没有删除它们,您可以将它们标记为“过时”。如果您发表类似“@user:我已更新我的帖子”之类的评论,那么评论者通常会回来并删除他们过时的 cmets...
  • 我将您的包装操作称为“模板重新绑定”。我想这是一个比较传统的术语,但不要指望它。
  • @KerrekSB,我已经更新了受您的 cmets 启发的问题,并将删除我的一条评论。
  • 您的地图包装器有些迂回,除非您的应用程序确实需要成员模板type。否则,您可以简单地为my_database 使用模板模板参数:template &lt;template &lt;typename K, typename T, typename ...Args&gt; class C&gt; struct my_database { C&lt;int, int&gt; x; }。不过,即使这样也不如将地图的实际 type 参数作为模板参数传递,因为这样更灵活。

标签: c++ templates nomenclature


【解决方案1】:

您提供了一个 模板,其中需要一个 type

就其价值而言,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’

(自从 Clang 开始产生非常好的错误消息以来,GCC 变得更好了,所以这可能是最近的;我想 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;

这是最通用的解决方案,因为您可以使用适合您需要的任何类,而无需该类具有任何特定结构。

作为替代方案(我不推荐),如果您希望所有容器都成为同一模板的模板特化,您可以将模板直接作为模板参数传入(a so -称为模板模板参数):

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

这更具限制性:不仅您的数据库现在必须对所有内容使用相同的数据结构,而且数据结构也必须作为具有固定前两个参数的模板给出“键类型”和“映射类型”。

【讨论】:

  • 实际上,我对我不知道的 template rebinding 这个名称很满意,作为答案,我会认为它是正确的。但是,您的最后一个解决方案绝对不适用(每个地图一个模板参数)。请注意,我的 database 是一个简化的示例代码。想象一下,我的数据库中使用了 1000 个不同的映射,我想从二叉树切换到散列。您最有帮助的建议是使用 clang,它向我表明我实际上可以在一种情况下将模板(而不是类型)作为参数传递,因此我可以修复我的最后一个示例。
  • @PatrickFromberg 这个答案(目前)没有提到“重新绑定”这个词,因为它不应该。该术语适用于允许将一种专业化转换为另一种专业化的模板,例如提供rebind 成员模板的std::allocator。我认为“包装类”就足够了。
  • @Potatoswatter,我明白你关于重新绑定的观点,这只是传递模板模板参数see here 的众多方法之一。但是“包装类”是通用的,因为有许多包装成语用于完全与问题无关的完全不同的目的。在您发表评论后,late template binding 将是我个人最喜欢的成语名称。
  • @PatrickFromberg 但是,如果您没有包装器,该术语也适用。无论如何,说你喜欢的:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多