模板参数的一些用途是什么?

模板参数的一些用途是什么?

我看到了一些使用模板参数(即以模板作为参数的模板)来进行基于策略的类设计的C+示例。这项技术还有什么其他用途?



繁星淼淼
浏览 669回答 3
3回答

Helenr

我认为您需要使用模板语法来传递一个参数,该参数的类型依赖于另一个模板,如下所示:template&nbsp;<template<class>&nbsp;class&nbsp;H,&nbsp;class&nbsp;S>void&nbsp;f(const&nbsp;H<S>&nbsp;&value)&nbsp;{}这里,H是一个模板,但我希望这个函数能够处理H.注:我已经编程c+很多年了,而且只需要一次。我发现它是一个很少需要的特性(当然,当您需要它时,它非常方便!)我一直在努力想出好的例子,老实说,大多数时候这是不必要的,但让我们想一个例子。让我们假装std::vector&nbsp;不有typedef value_type.那么,如何编写一个可以为向量元素创建正确类型的变量的函数呢?这样就行了。template&nbsp;<template<class,&nbsp;class>&nbsp;class&nbsp;V,&nbsp;class&nbsp;T,&nbsp;class&nbsp;A>void&nbsp;f(V<T,&nbsp;A>&nbsp;&v)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;This&nbsp;can&nbsp;be&nbsp;"typename&nbsp;V<T,&nbsp;A>::value_type", &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;but&nbsp;we&nbsp;are&nbsp;pretending&nbsp;we&nbsp;don't&nbsp;have&nbsp;it &nbsp;&nbsp;&nbsp;&nbsp;T&nbsp;temp&nbsp;=&nbsp;v.back(); &nbsp;&nbsp;&nbsp;&nbsp;v.pop_back(); &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Do&nbsp;some&nbsp;work&nbsp;on&nbsp;temp &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;temp&nbsp;<<&nbsp;std::endl;}注*我们std::vector有两个模板参数,类型和分配器,所以我们必须同时接受它们。幸运的是,由于类型推断,我们不需要显式地写出确切的类型。你可以这样用:f<std::vector,&nbsp;int>(v);&nbsp;//&nbsp;v&nbsp;is&nbsp;of&nbsp;type&nbsp;std::vector<int>&nbsp;using&nbsp;any&nbsp;allocator或者更好的是,我们可以用:f(v);&nbsp;//&nbsp;everything&nbsp;is&nbsp;deduced,&nbsp;f&nbsp;can&nbsp;deal&nbsp;with&nbsp;a&nbsp;vector&nbsp;of&nbsp;any&nbsp;type!更新:即使这个人为的例子,虽然具有说明性,但由于c+11的引入,已经不再是一个令人惊奇的例子了。auto..现在,可以将相同的函数编写为:template&nbsp;<class&nbsp;Cont>void&nbsp;f(Cont&nbsp;&v)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;temp&nbsp;=&nbsp;v.back(); &nbsp;&nbsp;&nbsp;&nbsp;v.pop_back(); &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Do&nbsp;some&nbsp;work&nbsp;on&nbsp;temp &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;temp&nbsp;<<&nbsp;std::endl;}这就是我更喜欢写这种类型的代码的方式。

临摹微笑

实际上,模板参数的使用是相当明显的。一旦您了解到C+stdlib存在不为标准容器类型定义流输出操作符的漏洞,就可以编写如下内容:template<typename&nbsp;T>static&nbsp;inline&nbsp;std::ostream&&nbsp;operator<<(std::ostream&&nbsp;out,&nbsp;std::list<T>&nbsp;const&&nbsp;v){ &nbsp;&nbsp;&nbsp;&nbsp;out&nbsp;<<&nbsp;'['; &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!v.empty())&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(typename&nbsp;std::list<T>::const_iterator&nbsp;i&nbsp;=&nbsp;v.begin();&nbsp;;)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out&nbsp;<<&nbsp;*i; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(++i&nbsp;==&nbsp;v.end()) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out&nbsp;<<&nbsp;",&nbsp;"; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;out&nbsp;<<&nbsp;']'; &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;out;}然后你会发现向量的代码是一样的,因为Forward_List是一样的,实际上,即使对于多种地图类型,它仍然是一样的。除了元接口/协议之外,这些模板类没有任何共同之处,使用模板参数可以捕获所有模板的共性。但是,在继续编写模板之前,值得检查一个引用,以回顾序列容器接受2个模板参数-用于值类型和分配器。虽然分配器是默认的,但我们仍然应该在模板操作符<:template<template&nbsp;<typename,&nbsp;typename>&nbsp;class&nbsp;Container,&nbsp;class&nbsp;V,&nbsp;class&nbsp;A>std::ostream&&nbsp;operator<<(std::ostream&&nbsp;out,&nbsp;Container<V,&nbsp;A>&nbsp;const&&nbsp;v)...瞧,这将自动工作,所有现在和未来的序列容器符合标准协议。要将映射添加到混合中,需要查看一下引用,注意它们接受4个模板参数,因此我们需要操作符<上面的另一个版本,使用4-Arg模板param。我们还会看到,std:偶对尝试使用2-Arg操作符<for我们前面定义的序列类型来呈现,因此我们将只为std:偶数提供专门化。顺便说一句,使用C+11(允许可变模板)(因此应该允许可变模板args),可以让单个操作符<来统治所有这些模板。例如:#include&nbsp;<iostream>#include&nbsp;<vector>#include&nbsp;<deque>#include&nbsp;<list>template<typename&nbsp;T,&nbsp;template<class,class...>&nbsp;class&nbsp;C,&nbsp;class...&nbsp;Args>std::ostream&&nbsp;operator&nbsp;<<(std::ostream&&nbsp;os,&nbsp;const&nbsp;C<T,Args...>&&nbsp;objs){ &nbsp;&nbsp;&nbsp;&nbsp;os&nbsp;<<&nbsp;__PRETTY_FUNCTION__&nbsp;<<&nbsp;'\n'; &nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(auto&nbsp;const&&nbsp;obj&nbsp;:&nbsp;objs) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;os&nbsp;<<&nbsp;obj&nbsp;<<&nbsp;'&nbsp;'; &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;os;}int&nbsp;main(){ &nbsp;&nbsp;&nbsp;&nbsp;std::vector<float>&nbsp;vf&nbsp;{&nbsp;1.1,&nbsp;2.2,&nbsp;3.3,&nbsp;4.4&nbsp;}; &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;vf&nbsp;<<&nbsp;'\n'; &nbsp;&nbsp;&nbsp;&nbsp;std::list<char>&nbsp;lc&nbsp;{&nbsp;'a',&nbsp;'b',&nbsp;'c',&nbsp;'d'&nbsp;}; &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;lc&nbsp;<<&nbsp;'\n'; &nbsp;&nbsp;&nbsp;&nbsp;std::deque<int>&nbsp;di&nbsp;{&nbsp;1,&nbsp;2,&nbsp;3,&nbsp;4&nbsp;}; &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;di&nbsp;<<&nbsp;'\n'; &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;}输出量std::ostream&nbsp;&operator<<(std::ostream&nbsp;&,&nbsp;const&nbsp;C<T,&nbsp;Args...>&nbsp;&)&nbsp;[T&nbsp;=&nbsp;float,&nbsp;C&nbsp;=&nbsp;vector,&nbsp;Args&nbsp;=&nbsp;<std::__1::allocator<float>>]1.1&nbsp;2.2&nbsp;3.3&nbsp;4.4&nbsp;std::ostream&nbsp;&operator<<(std::ostream&nbsp;&,&nbsp;const&nbsp;C<T,&nbsp;Args...>&nbsp;&)&nbsp;[T&nbsp;=&nbsp;char,&nbsp;C&nbsp;=&nbsp;list,&nbsp;Args&nbsp;=&nbsp;<std::__1::allocator<char>>]a&nbsp;b&nbsp;c&nbsp;d&nbsp; std::ostream&nbsp;&operator<<(std::ostream&nbsp;&,&nbsp;const&nbsp;C<T,&nbsp;Args...>&nbsp;&)&nbsp;[T&nbsp;=&nbsp;int,&nbsp;C&nbsp;=&nbsp;deque,&nbsp;Args&nbsp;=&nbsp;<std::__1::allocator<int>>]1&nbsp;2&nbsp;3&nbsp;4

天涯尽头无女友

下面是一个简单的例子“现代C+设计-通用编程和应用设计模式”安德烈·亚历山德雷斯库:他使用带有模板参数的类来实现策略模式://&nbsp;Library&nbsp;codetemplate&nbsp;<template&nbsp;<class>&nbsp;class&nbsp;CreationPolicy>class&nbsp;WidgetManager&nbsp;:&nbsp;public&nbsp;CreationPolicy<Widget>{ &nbsp;&nbsp;&nbsp;...};他解释说:通常,宿主类已经知道或可以很容易地推断策略类的模板参数。在上面的示例中,WidgetManager总是管理Widget类型的对象,因此要求用户在CreationPolicy的实例化中再次指定Widget是多余的,而且可能很危险。在这种情况下,库代码可以使用模板参数来指定策略。其效果是客户端代码可以更优雅地使用“WidgetManager”:typedef&nbsp;WidgetManager<MyCreationPolicy>&nbsp;MyWidgetMgr;而不是缺乏模板参数的定义所需要的更麻烦、更容易出错的方式:typedef&nbsp;WidgetManager<&nbsp;MyCreationPolicy<Widget>&nbsp;>&nbsp;MyWidgetMgr;
打开App,查看更多内容
随时随地看视频慕课网APP