似乎理解模板参数会要了我的命:(,让我解释一下我在头脑中产生了什么误解,这让我感到困惑:
template<class T>
class B {}; // A templated class下面是其他代码:
template<template<class X> class Z = B> // The problem is in this line for me
class BB{};请注意模板化类BB的参数列表中的一行,即:
template<class X> class Z = B现在,是什么阻止了C++认为Z不是另一个模板化的类Z?
也就是说,
template<class X> class Z {
}而不是认为类Z本身就是一个模板化的参数。
发布于 2011-06-27 01:16:28
Mankarse has answered你的问题,但我想我应该插话。
模板模板参数与普通模板类型参数类似,不同之处在于它们与模板匹配,而不是与具体类型匹配:
// 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;
};如果有帮助,你可以把它们想象成函数指针。普通函数只接受参数,就像普通模板只接受类型一样。但是,有些函数接受接受参数的函数指针,就像模板类型接受接受类型的模板一样:
void foo(int x)
{
cout << x << endl;
}
void bar(void (*f)(int))
{
f(1);
f(2);
}在评论中回答你的问题:模板参数是不可能的。然而,它们不可能的原因只是因为标准化委员会认为模板模板就足够了,可能是为了让编译器实现者的生活更容易。也就是说,没有什么能阻止委员会决定它们是可能的,那么像这样的事情就是有效的C++:
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。
types <=> values
templates <=> functions of values
template templates <=> functions of functions of values
template template templates <=> functions of functions of functions of values与Baz类似的功能是:
void baz(void (*g)(void (*f)(int)))
{
g(foo);
}您将在何处使用模板?
这很牵强,但我能想到一个例子:一个真正通用的图形搜索库。
图搜索中的两种常见算法是深度优先搜索(DFS)和广度优先搜索(BFS)。这两种算法的实现是相同的,除了一个方面: DFS使用节点堆栈,而BFS使用队列。理想情况下,我们只需编写一次算法,并使用堆栈/队列作为参数。此外,我们还希望指定堆栈或队列的实现容器,以便我们可以执行以下操作:
search<Stack, Vector>( myGraph ); // DFS
search<Queue, Deque>( myGraph ); // BFS但是什么是堆栈或队列呢?嗯,就像在STL中一样,堆栈或队列可以用任何类型的容器实现:向量,双队列,列表等,也可以是任何元素类型的堆栈,所以我们的堆栈或队列将具有以下接口:
Stack<Vector, int> // stack of ints, using a vector implementation
Queue<Deque, bool> // queue of bools, using a deque implementation但是Vector和Deque本身就是模板类型!
最后,我们的Stack将是一个模板模板,如下所示:
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算法就必须是模板!
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++,所以整个图搜索的东西实际上不会编译。这只是一个“如果呢?”:)
发布于 2011-06-26 22:19:46
这是该语言语法的一部分(这是可怕的,并且大量依赖于上下文)。如果template<class X> class Z出现在模板参数列表中,那么它将被解释为形式参数Z的声明,带有种类(如元类型;种类分类类型的方式与类型分类值的方式相同)“带有一个类参数的模板类”。
发布于 2021-03-20 19:55:24
接受答案中的用法示例具有误导性,
尤其是对于初学者。诚然,很难想出任何不是人为设计的东西,但我们至少应该设计出不违反总体原则的东西。只有当我们界面的用户由于某种原因不能指定模板的类型时,才应该使用模板参数,而我们需要为它们指定模板类型。在堆栈示例中,我们同时请求Storage和Element,只是为了使用该元素实例化Storage,这是完全不必要的,用户可以轻松地执行基本替换:
Stack<deque<int>> my_stack;所有堆栈需要做的就是:
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(); }
};它不会以任何方式为用户决定元素类型,因此它不需要模板参数。因此搜索就变成了
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?所以让我们想象一下,他们可以访问一些节点引用/指针类型,然后他们可以这样做:
search<Stack<vector<Graph::node_ptr>>>(graph, 10);该函数进一步简化为:
template <template StackStructure, typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
StackStructure data;
// do algorithm
}天哪,该死的,现在它比以前更通用了!是否要为存储指定分配器?没问题,照做就行了。相反,您需要一些需要最大大小参数的静态分配向量?请用吧。想要从头开始实现堆栈吗?好吧,只要它叫起来像堆一样……
也许是一个更合适的例子
具有模板参数的模板将是表示复杂系统的某个类,并将某个存储模板用于一组内部结构,并且出于某种原因在该存储模板上进行参数化:
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吧,这是一个更复杂的类,它使用了我们的许多系统,但关键是需要自己决定每个系统使用什么存储模板。
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
根据我的经验,只有在元编程库中有齐膝深的模板参数时,您才会遇到很好的模板用例。一条经验法则:如果你能识别出这种模式
template<...> struct f { typedef ... type; };作为一个类型的函数,那么在这种心态下,你可以使用带有模板参数的模板,并可能思考更深层次的东西。否则就拍你自己的手腕。
https://stackoverflow.com/questions/6484484
复制相似问题