首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模板模板参数

模板模板参数
EN

Stack Overflow用户
提问于 2011-06-26 22:14:01
回答 3查看 34.2K关注 0票数 47

似乎理解模板参数会要了我的命:(,让我解释一下我在头脑中产生了什么误解,这让我感到困惑:

代码语言:javascript
复制
template<class T>
class B {}; // A templated class

下面是其他代码:

代码语言:javascript
复制
template<template<class X> class Z = B> // The problem is in this line for me
class BB{};

请注意模板化类BB的参数列表中的一行,即:

代码语言:javascript
复制
template<class X> class Z = B

现在,是什么阻止了C++认为Z不是另一个模板化的类Z?

也就是说,

代码语言:javascript
复制
template<class X> class Z {
}

而不是认为类Z本身就是一个模板化的参数。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-06-27 01:16:28

Mankarse has answered你的问题,但我想我应该插话。

模板模板参数与普通模板类型参数类似,不同之处在于它们与模板匹配,而不是与具体类型匹配:

代码语言:javascript
复制
// Simple template class
template <typename Type>
class Foo
{
    Type m_member;
};

// Template template class
template <template <typename Type> class TemplateType>
class Bar
{
    TemplateType<int> m_ints;
};

如果有帮助,你可以把它们想象成函数指针。普通函数只接受参数,就像普通模板只接受类型一样。但是,有些函数接受接受参数的函数指针,就像模板类型接受接受类型的模板一样:

代码语言:javascript
复制
void foo(int x)
{
    cout << x << endl;
}

void bar(void (*f)(int))
{
    f(1);
    f(2);
}

在评论中回答你的问题:模板参数是不可能的。然而,它们不可能的原因只是因为标准化委员会认为模板模板就足够了,可能是为了让编译器实现者的生活更容易。也就是说,没有什么能阻止委员会决定它们是可能的,那么像这样的事情就是有效的C++:

代码语言:javascript
复制
template <template <template <typename> class> class TemplateTemplateType>
class Baz
{
    TemplateTemplateType<Foo> m_foos;
};

typedef Baz<Bar> Example;
// Example would then have Bar<Foo> m_foos;
// which would have Foo<int> m_ints;

同样,您可以在函数指针中看到parallels。

代码语言:javascript
复制
                      types <=> values
                  templates <=> functions of values
         template templates <=> functions of functions of values
template template templates <=> functions of functions of functions of values

Baz类似的功能是:

代码语言:javascript
复制
void baz(void (*g)(void (*f)(int)))
{
    g(foo);
}

您将在何处使用模板?

这很牵强,但我能想到一个例子:一个真正通用的图形搜索库。

图搜索中的两种常见算法是深度优先搜索(DFS)和广度优先搜索(BFS)。这两种算法的实现是相同的,除了一个方面: DFS使用节点堆栈,而BFS使用队列。理想情况下,我们只需编写一次算法,并使用堆栈/队列作为参数。此外,我们还希望指定堆栈或队列的实现容器,以便我们可以执行以下操作:

代码语言:javascript
复制
search<Stack, Vector>( myGraph ); // DFS
search<Queue, Deque>( myGraph ); // BFS

但是什么是堆栈或队列呢?嗯,就像在STL中一样,堆栈或队列可以用任何类型的容器实现:向量,双队列,列表等,也可以是任何元素类型的堆栈,所以我们的堆栈或队列将具有以下接口:

代码语言:javascript
复制
Stack<Vector, int> // stack of ints, using a vector implementation
Queue<Deque, bool> // queue of bools, using a deque implementation

但是VectorDeque本身就是模板类型!

最后,我们的Stack将是一个模板模板,如下所示:

代码语言:javascript
复制
template <template <typename> class Storage, typename Element>
struct Stack
{
    void push(const Element& e) { m_storage.push_back(e); }
    void pop() { m_storage.pop_back(); }
    Storage<Element> m_storage;
};

然后我们的search算法就必须是模板!

代码语言:javascript
复制
template <template <template <typename> class, typename> class DataStructure,
          template <typename> class Storage,
          typename Graph>
void search(const Graph& g)
{
    DataStructure<Storage, typename Graph::Node> data;
    // do algorithm
}

这将是相当激烈的,但希望您能理解。

记住:模板不是合法的C++,所以整个图搜索的东西实际上不会编译。这只是一个“如果呢?”:)

票数 82
EN

Stack Overflow用户

发布于 2011-06-26 22:19:46

这是该语言语法的一部分(这是可怕的,并且大量依赖于上下文)。如果template<class X> class Z出现在模板参数列表中,那么它将被解释为形式参数Z的声明,带有种类(如元类型;种类分类类型的方式与类型分类值的方式相同)“带有一个类参数的模板类”。

票数 18
EN

Stack Overflow用户

发布于 2021-03-20 19:55:24

接受答案中的用法示例具有误导性,

尤其是对于初学者。诚然,很难想出任何不是人为设计的东西,但我们至少应该设计出不违反总体原则的东西。只有当我们界面的用户由于某种原因不能指定模板的类型时,才应该使用模板参数,而我们需要为它们指定模板类型。在堆栈示例中,我们同时请求Storage和Element,只是为了使用该元素实例化Storage,这是完全不必要的,用户可以轻松地执行基本替换:

代码语言:javascript
复制
Stack<deque<int>> my_stack;

所有堆栈需要做的就是:

代码语言:javascript
复制
template <template class Storage>
struct Stack
{
    void push(typename Storage::const_reference e) { m_storage.push_back(e); }
    void pop() { m_storage.pop_back(); }
    Storage m_storage;
    typename Storage::reference top() { return m_storage.back(); }
};

它不会以任何方式为用户决定元素类型,因此它不需要模板参数。因此搜索就变成了

代码语言:javascript
复制
template <template <typename> class DataStructure,
      template <typename> class Storage,
      typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
    DataStructure<Storage<typename Graph::Node>> data;
    // do algorithm
}

在这里,我猜我们假设内部的Graph::Node类型对用户是不可访问的,而搜索是Graph的一个朋友函数,这似乎有一定的意义。然而,我们是否真的需要用图节点填充结构,或者只是简单地引用它们?用户可以不以任何方式引用节点吗?如果不是,为什么它被称为图,而不是slow_unordered_set?所以让我们想象一下,他们可以访问一些节点引用/指针类型,然后他们可以这样做:

代码语言:javascript
复制
search<Stack<vector<Graph::node_ptr>>>(graph, 10);

该函数进一步简化为:

代码语言:javascript
复制
template <template StackStructure, typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
    StackStructure data;
    // do algorithm
}

天哪,该死的,现在它比以前更通用了!是否要为存储指定分配器?没问题,照做就行了。相反,您需要一些需要最大大小参数的静态分配向量?请用吧。想要从头开始实现堆栈吗?好吧,只要它叫起来像堆一样……

也许是一个更合适的例子

具有模板参数的模板将是表示复杂系统的某个类,并将某个存储模板用于一组内部结构,并且出于某种原因在该存储模板上进行参数化:

代码语言:javascript
复制
template <template <typename> class Storage>
class System
{
    Storage<Component_1> components_1;
    Storage<Component_2> components_2;
    Storage<Component_3> components_3;        
    Storage<MetaInfo> registry;
    public:
    // some inane interface
};

如果你问我--这段代码很臭,但我并不是不想写。

现在我们有了一个带有模板参数的模板的半合适的例子,我们可以为一个带有模板参数的模板设计一些东西,这个模板参数本身有一个模板参数:想象一下,我们以某种方式结束了大约10个这样的系统类,它们都有相同的接口,都在存储模板上参数化,但在其他方面非常不同。准备好迎接SuperSystem吧,这是一个更复杂的类,它使用了我们的许多系统,但关键是需要自己决定每个系统使用什么存储模板。

代码语言:javascript
复制
template< template< template <typename> class Storage> class System>
class SuperSystem
{
    System<Vector> system_1;
    System<OtherVector> system_2;themselves
    System<List> system_3;
    public:
    // absolutely bonkers interface
};

我们希望在模板层次结构中指定一些内容,但仍然要在层次结构中保留一些可自定义的内容。由于某些原因,我们不知道我们将处理的确切系统,但我们知道关于所有这些系统的一些非常具体的东西,我们绝对需要走自己的路。通过这些例子,这是一个重要的主题,我们的目标不是让事情变得更加通用和可定制,而是相反的--我们想锁定某些深嵌入的东西。

TL;DR

根据我的经验,只有在元编程库中有齐膝深的模板参数时,您才会遇到很好的模板用例。一条经验法则:如果你能识别出这种模式

代码语言:javascript
复制
template<...> struct f { typedef ... type; };

作为一个类型的函数,那么在这种心态下,你可以使用带有模板参数的模板,并可能思考更深层次的东西。否则就拍你自己的手腕。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6484484

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档