template(4.1)

5高阶基本技术(Tricky Basics)

本章涵盖实际编程之中层次较高的一些 template基本知识,包括关键词 typename的另一种用途、将member
functio(n 成员函数)和 nested clas(s 嵌套类别)定为templates、奇特的 template
template parameters、零值初始化(zero initialization)、以字符串字面常数(string literals)作为
function templates arguments
的细节问题…等等。有时候这些问题可能涉及较多技巧,但每一位程序员都应该至少对它们有大略的了解。

5.1
关键词 typename

关键词typename 是C++标准化过程中被引入的,目的在于向编译器说明template
内的某个标识符是个类型(而不是其它什么东西)。考虑下面的例子:


template <typename T>
class MyClass {
  typename T::SubType * ptr;
  ...
};

在这里,第二个
typename 关键词的意思是:SubType 是 class T 内部定义的一个类型,从而ptr是一个「指向 T::SubType
类型」的指针。如果上例没有使用关键词 typename,SubType 会被认为是 class T 的一个 static 成员,于是被编
译器理解为一个具体变量或一个对象,导致以下式子:

T::SubType
* ptr

所表达的意义变成:class
T 的 static 成员 SubType 与 ptr 相乘。通常 如果 某个与 template parameter 相关的名称是个 类型( type)
时,你就 必须加上关键字typename。更详细的讨论见 9.3.2 节。

typename 的一个典型应用是在 template 程序代码中使用「STL
容器供应的迭代器(iterators)」:

// basics/printcoll.hpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
#include <iostream>

// print elements of an STL container
template <typename T>
void printcoll (T const& coll)
{
typename T::const_iterator pos; // iterator to iterate over coll
typename T::const_iterator end(coll.end()); // end position

for (pos=coll.begin(); pos!=end; ++pos) {
std::cout << *pos << ‘ ‘;
}
std::cout << std::endl;
}

在这个 function template 中,coll 是个 STL 容器,其元素类型为
T。这里使用了 STL 容器的迭代器类型(iterator type)遍历 coll
的所有元素。迭代器类型为const_iterator,每一个STL 容器都声明有这个类型:


class stlcontainer {
...
  typedef ... iterator; // 可读可写的迭代器
  typedef ... const_iterator; // 惟读迭代器
  ...
};

使用
template type T 的 const_iterator 时,你必须写出全名,并在最前面加上关键词 typename:typename
T::const_iterator pos;

.template
构件 ( construct)引入关键词 typename 之后,人们又发现了一个类似问题。考虑以下程序代码,其中使用标准的bitset
类型:


template<int N>
void printBitset (std::bitset<N> const& bs)
{
std::cout << bs.template to_string<char,char_traits<char>,allocator<char> >();
}

此例中的 .template 看起来有些古怪,但是如果没有它,编译器无法得知紧跟其后的
"<" 代表的是个 template argument list 的起始,而非一个「小于」符号。注意,只有当位于 "."
之前的构件(construct)取决于某个 template parameter 时,这个问题才会发生。以上例子中,参数 bs 便是取决(受控)于
template parameter N。结论是
".template" 或 "->template" 记号只在 templates 之内才能被使用,而且它们必须紧 跟着「与 template
parameters 相关」的某物体。细节请见 9.3.3 节, p.132。

5.2 使用 this->

如果 class templates 拥有 base classes,那么其内出现的成员名称 x 并非总是等价于
this->x,即使 x 系继承而来。例如:template <typename T>


class Base {
public:
  void exit();
};

template <typename T>
class Derived : public Base<T> {
public:
  void foo() {
  exit();
}
};

本例在 foo()内解析(resolving)"exit" 符号时,定义于 Base
的exit()会被编译器忽略。因此,你要么获得一个编译错误,要么就是调用一个外部的 exit()。

我们将在 9.4.2 节详细讨论这个问题。目前可视为一个准则:使用与
template 相关的符号时,建议总是以this-> 或Base<T>:: 进行修饰。为避免任何不确定性,可考虑在templates
内对所有成员存取动作(member accesses)进行以上修饰。

5.3 Member Templates(成 员模板 )

Class的成员也可以是templates:既可以是nested class templates,也可以是member
function templates。让我再次使用 Stack<> class templates
来展示这项技术的优点,并示范如何运用这种技术。通常只有当两个 stacks 类型相同,也就是当两个stacks
拥有相同类型的元素时,你才能对它们相互赋值(assign),也就是将某个 stack 整体赋值给另一个。你不能把某种类型的
stack赋值给另一种类型的stack,即使这两种类型之间可以隐式转型:


Stack<int> intStack1, intStack2; // stacks for ints
Stack<float> floatStack; // stack for floats
...
intStack1 = intStack2; // OK:两个 stacks 拥有相同类型
floatStack = intStack1; // ERROR:两个 stacks 类型不同
default assignment 运算符要求左右两边拥有相同类型,而以上情况中,拥有不同类型元素的两个 stacks,其类型并不相同。然而,只要把 assignment 运算符定义为一个 template,你就可以让两个「类型不同,但其元素 可隐式转型」的 stacks 互相赋值。为完成此事,Stack<> 需要这样的声明:


// basics/stack5decl.cpp
template <typename T>
class Stack {
private:
  std::deque<T> elems; // 元素
public:
  void push(T const&); // push 元素
  void pop(); // pop 元素
  T top() const; // 传回 stack 顶端元素
  bool empty() const { // stack 是否为空
  return elems.empty();
}

// 以「元素类型为 T2」的 stack 做为赋值运算的右手端。
template <typename T2>
Stack<T>& operator= (Stack<T2> const&);
};

对比原先的
Stack,这个版本有如下改动:

1.
增加一个 assignment 运算符,使 Stack 可被赋予一个「拥有不同元素类型 T2」的 stack。

2.
这个 stack 如今使用 deque 作为内部容器。这个改动是上述改动的连带影响。

新增加的 assignment 运算符实作如下:

//
basics/stack5assign.hpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
template <typename T>
template <typename T2>
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2)
{
if ((void*)this == (void*)&op2) { // assignment to itself?
return *this;
}

Stack<T2> tmp(op2); // create a copy of the assigned stack

elems.clear(); // remove existing elements
while (!tmp.empty()) { // copy all elements
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}

我们先看看什么样的语法可以定义一个 member template。在拥有
template parameter T 的 template中定义一个内层的(inner)template parameter T2:


template <typename T>
template <typename T2>
...

实作这个运算符(成员函数)时,你可能希望取得「赋值符号右侧之op2
stack」的所有必要资料,但是这个stack的类型和目前(此身)类型不同(是的,如果你以两个不同的类型实例化同一个class template,你会得到两个不同的类型),所以只能通过public接口来得到那些数据。
因此惟一能够取得 op2 数据的办法就是调用其 top()函数。你必须经由 top()取得op2 的所有数据,而这必须借助 op2
的一份拷贝来实现:每取得一笔数据,就运用 pop()把该数据从op2
的拷贝中移除。由于top()传回的是stack之中最后(最晚)被推入的元素,所以我们需要反方向把元素安插回去。基于这种需求,这里使用了deque,它提供push_front()操作,可以把一
个元素安插到容器最前面。有了这个 member template,你就可以把一个 int stack 赋值(assign)给一个 float
stack:


Stack<int> intStack1, intStack2; //stack for ints
Stack<float> floatStack; //stack for floats
...
floatStack = intStack1; // OK:两个 stacks 类型不同,但 int 可转型为 float。

当然,这个赋值动作并不会改动 stack和其元素的类型。赋值完成后,floatStack
的元素类型还是 float,而 top()仍然传回 float
值。也许你会认为,这么做会使得类型检查失效,因为你甚至可以把任意类型的元素赋值给另一个 stack。然而事实并非如此。必要的类型检查会在「来源端
stack」的元素(拷贝)被安插到「目 的端 stack」时进行:

elems.push_front(tmp.top());

如 果 你将一个 string stack 赋值 给一个 float stack , 以 上 述 句 编译时就会发生错 误:
「elems.push_front()无法接受 tmp.top()的返回类型」。具体的错误讯息因编译器而异,但含义类似。


Stack<std::string> stringStack; // stack of strings
Stack<float> floatStack; // stack of floats

floatStack = stringStack; // 错误:std::string 无法转型为 float

注意,前述的 template assignment 运算符并不取代 default assignment
运算符。如果你在相同类型的 stack 之间赋值,编译器还是会采用 default assignment 运算符。

和先前一样,你可以把内部容器的类型也参数化:

//
basics/stack6decl.hpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
template <typename T, typename CONT = std::deque<T> >
class Stack {
private:
CONT elems; // elements

public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}

// assign stack of elements of type T2
template <typename T2, typename CONT2>
Stack<T,CONT>& operator= (Stack<T2,CONT2> const&);
};

此时的 template assignment 运算符可实作如下:

//
basics/stack6assign.hpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
template <typename T, typename CONT>
template <typename T2, typename CONT2>

Stack<T,CONT>&
Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2)
{
if ((void*)this == (void*)&op2) { // assignment to itself?
return *this;
}

Stack<T2,CONT2> tmp(op2); // create a copy of the assigned stack

elems.clear(); // remove existing elements
while (!tmp.empty()) { // copy all elements
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}

记住,对 class templates
而言,只有「实际被调用的成员函数」才会被实例化。因此如果你不至于令不同(元素)类型的 stacks 彼此赋值,那么甚至可以拿 vector
当作内部元素的容器(译注: 而先前的程序代码完全不必改动):

// stack for ints,使用 vector 作为内部容器


Stack<int,std::vector<int> > vStack;
  ...
  vStack.push(42
);
vStack.push(7);
std::cout << vStack.top() << std::endl;

由于 template assignment
运算符并未被用到,编译器不会产生任何错误讯息抱怨说「内部容器 无法支持 push_front()操作」。以上例子的完整实作全部包含于以
stack6 开头的文件中,位于子目录 basics 之下。

5.4
Template Template Parameters(双 重 模板参 数)

一个 template parameter 本身也可以是个 class template,这一点非常有用。我们将再次以
stack class template 说明这种用法。

为了使用其它类型的元素容器,stack class 使用者必须两次指定元素类型:一次是元素类型本身, 另一次是容器类型:


Stack<int,std::vector<int> > vStack; // int stack,以 vector 为容器,如果使用 template template parameter,就可以只指明元素类型,无须再指定容器类型:
Stack<int,std::vector> vStack; // int stack,以 vector 为容器

为了实现这种特性,你必须把第二个
template parameter 声明为 template template parameter。

原 则上程序代码可以写为:

// basics/stack7decl.cpp


template <typename T,template <typename ELEM> class CONT = std::deque >
class Stack {
private:
  CONT<T> elems; // 元素
public:
  void push(T const&); // push 元素
  void pop(); // pop 元素
  T top() const; // 传回 stack 顶端元素
  bool empty() const { // stack 是否为空
  return elems.empty();
  }
};

与先前的 stack 差别在于,第二个 template parameter 被声明为一个 class
template:template <typename ELEM> class CONT其默认值则由 std::deque<T> 变更为
std::deque。这个参数必须是个 class template,并以第一参数的类型完成实例化:CONT<T> elems;

本例「以第一个 template parameter 对第二个 template parameter
进行实例化」只是基于例子本身的需要。实际运用 时你可以使用 class template 内的任何类型来实例化一个 template template
parameter。和往 常一 样,你也 可以改 用 关键词 class 而不使 用关键 字 typename来声明一个template parameter;但
CONT 定义的是一个 class 类型,因此你必须使用关键词 class 来声明它。所以,


//下面的程序代码是正确的:
template <typename T,template <typename ELEM> class CONT = std::deque > //OK
class Stack {
  ...
};

//下面的程序代码则是错误的:
template <typename T,template <typename ELEM> typename CONT = std::deque > //ERROR
class Stack {
  ...
};

由于 template template parameter 中的 template
parameter 实际并未用到,因此你可以省略其名称:


template <typename T,template <typename> class CONT = std::deque >
class Stack {
  ...
};

所有成员函数也必须按此原则修改:必须指定其第二个template
parameter为template template parameter。同样的规则也适用于成员函数的实作部份。例如成员函数
push()应该实作如下:


template <typename T, template <typename> class CONT>
void Stack<T,CONT>::push (T const& elem)
{
  elems.push_back(elem); // 追加元素
}

另请注意,function templates 不允许拥有 template
template parameters。Template Template Argument 的 匹配(matching)

如果你试图使用上述新版 Stack,编译器会报告一个错误:默认值 std::deque 不符合
template template parameter CONT 的要求。问题出在 template template argument 不但必须是个
template,而且 其参数 必须严格匹配它 所替换 之 template template parameter 的 参数。Template
template argument 的默认值不被考虑,因此如果不给出拥有默认值的自变量值时,编译器会认为匹配失败。

本例的问题在于:标准库中的 std::deque template
要求不只一个参数。第二参数是个配置器(allocator),它虽有默认值,但当它被用来匹配CONT
的参数时,其默认值被编译器强行忽略了。办法还是有的。我们可以重写 class 声明语句,使 CONT 参数要求一个「带两个参数」的容器:


template <typename T,template <typename ELEM,typename ALLOC = std::allocator<ELEM> >
class CONT = std::deque>
class Stack {
private:
  CONT<T> elems; // 元素
  ...
};

由于 ALLOC 并未在程序代码中用到,因此你也可以把它省略掉。Stack
template 的最终版本如下。此一版本支持对「不同元素类型」之 stacks 的彼此赋值动作:

//
basics/stack8.hpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
#ifndef STACK_HPP
#define STACK_HPP

#include <deque>
#include <stdexcept>
#include <memory>

template <typename T,
template <typename ELEM,
typename = std::allocator<ELEM> >
class CONT = std::deque>
class Stack {
private:
CONT<T> elems; // elements

public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return whether the stack is empty
return elems.empty();
}

// assign stack of elements of type T2
template<typename T2,
template<typename ELEM2,
typename = std::allocator<ELEM2>
>class CONT2>
Stack<T,CONT>& operator= (Stack<T2,CONT2> const&);
};

template <typename T, template <typename,typename> class CONT>
void Stack<T,CONT>::push (T const& elem)
{
elems.push_back(elem); // append copy of passed elem
}

template<typename T, template <typename,typename> class CONT>
void Stack<T,CONT>::pop ()
{
if (elems.empty()) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
elems.pop_back(); // remove last element
}

template <typename T, template <typename,typename> class CONT>
T Stack<T,CONT>::top () const
{
if (elems.empty()) {
throw std::out_of_range("Stack<>::top(): empty stack");
}
return elems.back(); // return copy of last element
}

template <typename T, template <typename,typename> class CONT>
template <typename T2, template <typename,typename> class CONT2>
Stack<T,CONT>&
Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2)
{
if ((void*)this == (void*)&op2) { // assignment to itself?
return *this;
}

Stack<T2,CONT2> tmp(op2); // create a copy of the assigned stack

elems.clear(); // remove existing elements
while (!tmp.empty()) { // copy all elements
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}

#endif // STACK_HPP

下面程序使用了上述最终版本的 stack:

// basics/stack8test.cpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include "stack8.hpp"

int main()
{
try {
Stack<int> intStack; // stack of ints
Stack<float> floatStack; // stack of floats

// manipulate int stack
intStack.push(42);
intStack.push(7);

// manipulate float stack
floatStack.push(7.7);

// assign stacks of different type
floatStack = intStack;

// print float stack
std::cout << floatStack.top() << std::endl;
floatStack.pop();
std::cout << floatStack.top() << std::endl;
floatStack.pop();
std::cout << floatStack.top() << std::endl;
floatStack.pop();
}
catch (std::exception const& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
}

// stack for ints using a vector as an internal container
Stack<int,std::vector> vStack;
//...
vStack.push(42);
vStack.push(7);
std::cout << vStack.top() << std::endl;
vStack.pop();
}

程序运行的输出结果为:

7

42

Exception:
Stack<>::top(): empty stack

7

注意,template template parameter 是极晚近才加入的C++
特性,因此上面这个程序可作为一个极佳工具,用来评估你的编译器对 template 特性的支持程度。

5.5
零值初始化( Zero Initialization)

对于基本类型如 int、double、pointer type(指针类型)来说,并没有一个 default 构造函数将它
们初始化为有意义的值。任何一个未初始化的区域变量(local variable),其值都是未定义的:


void foo()
{
  int x; // x 的值未有定义
  int* ptr; // ptr 指向某处(而不是哪儿都不指向)
}

你可能在 template
程序代码中声明某个变量,并且想令这个变量被初始化为其默认值;但是当变 数是个内建类型(built-in
type)时,你无法确保它被正确初始化:


template <typename T>
void foo()
{
  T x; // 如果 T 是内建类型,则 x 值未有定义
}

为解决这个问题,你可以在声明内建类型的变量时,明确调用其 default 构造函数,使其值为零(对bool 类型而言则是
false)。也就是说 int()导致 0 值。这样一来你就可以确保内建类型的变 数有正确初值:


template <typename T>
void foo()
{
T x = T(); // 如果 T 是内建类型,则 x 被初始化为 0 或 false
}

Class template
的各个成员,其类型有可能被参数化。为确保初始化这样的成员,你必须定义一个构造函数,在其「成员初值列」(member initialization
list)中对每个成员进行初始化:


template <typename T>
class MyClass{
private:
  T x;
public:
  MyClass() : x() { // 这么做可以确保:即使 T 为内建类型,x 也能被初始化。
}
...
};

5.6 以字符串字面常数(String Literals)作为 Function
Template Arguments以 by reference 传递方式将「字符串字面常数」(string literals)传递给
function template parameters时,有可能遇上意想不到的错误:

//
basics/max5.cpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
#include <string>

// note: reference parameters
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}

int main()
{
std::string s;

::max("apple","peach"); // OK: same type
::max("apple","tomato"); // ERROR: different types
::max("apple",s); // ERROR: different types
}

问题出在这几个字符串字面常数(string literals)的长度不同,因而其底层的
array 类型也不同。换句话说 "apple" 和 "peach" 的 array 类型都是 char const[6],而 "tomato"
的 array型别是char const[7]。上述调用只有第一个合法,因为两个参数具有相同类型;然而如果你使用 by value
传递方式,就可以传递不同类型的字符串字面常数(string iterals),其对应的 array大小不同:

//
basics/max6.hpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
#include <string>

// note: nonreference parameters
template <typename T>
inline T max (T a, T b)
{
return a < b ? b : a;
}

int main()
{
std::string s;

::max("apple","peach"); // OK: same type
::max("apple","tomato"); // OK: decays to same type
::max("apple",s); // ERROR: different types
}

这种方式之所以可行,因为在自变量推导过程中,惟有当参数并不是一个 reference
类型时,「array 转为 pointer」的转型动作(常被称为退化,
decay)才会发生。这个规则可藉以下例子加以说明:

//
basics/refnonref.cpp


/* The following code example is taken from the book
* "C++ Templates - The Complete Guide"
* by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
*
* (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
* Permission to copy, use, modify, sell and distribute this software
* is granted provided this copyright notice appears in all copies.
* This software is provided "as is" without express or implied
* warranty, and with no claim as to its suitability for any purpose.
*/
#include <typeinfo>
#include <iostream>

template <typename T>
void ref (T const& x)
{
std::cout << "x in ref(T const&): "
<< typeid(x).name() << ‘\n‘;
}

template <typename T>
void nonref (T x)
{
std::cout << "x in nonref(T): "
<< typeid(x).name() << ‘\n‘;
}

int main()
{
ref("hello");
nonref("hello");
}

在这个例子中,同一个字符串字面常数(string literal)分别被传递给两个
function templates,其一声明参数为 reference,其二声明参数为 non-reference。两个函数都使用 typeid
运算符打印其具现化后的参数类型。typeid 运算符会传回一个左值(lvalue),其类型为
std::type_info,其内封装了「typeid 运算符所接收之算式(expression)」的类型表述(representation)。std::type_info
的成员函式 name()把这份「类型表述」以易读的字符串形式传回给调用者。C++ Standard 并不要求name()
传回有意义的内容,但是在良好的 C++ 编译器中,它会传回一个字符串内含「typeid运算符的
参数类型」的完好描述。在某些编译器实作品中,这个字符串可能以重排(mangled)形式出现,但也有些反重排工具(demangler)可以把它调整回人类可读的形式。例如上面程序的输出可能如下:


x in ref(T const&): char [6]
x in nonref(T): const char *

如果你曾经在程序中把「char array」和「char
pointer」混用,你可能被这个令人惊奇的问题搞得头昏脑胀。不幸的是,没有一个普遍适用的办法可以解决这个问题。根据所处情况

的不同,你可以:

 
以by value 传递方式代替by reference 传递方式。然而这会带来不必要的拷贝。

 
分别对by value传递方式和by reference传递方式进行重载。然而这会带来模棱两可问题(ambiguities, 歧义性),请参考 B.2.2
节。

 
以具体类型(例如 std::string)重载之  以 array 类型重载之。例如:


template <typename T, int N, int M>
T const* max (T const (&a)[N], T const (&b)[M])
{
return a < b ? b : a;
}

  强迫使用者进行显式转型(explicit
conversions)本例之中,最好的方式是对 string 重载(请参考 2.4
节)。这么做有其必要。如果不这么做,对两个字符串字面常数(string literals)调用 max()是合法的,但 max()会以 operator<
比较两个指针的大小,而所比较的其实是指针的地址,不是两个字符串的字面值。这也是为什么使用 std::string 比使用 C-style
字符串更好的原因之一。

5.7
摘要

  当你要操作一个取决于(受控于)template parameter 的类型名称时,应该在其前面冠以关键字
typename。嵌套类别(nested classes)和成员函数(member functions)也可以是 templates。应用之一是,
你可以对「不同类型但彼此可隐式转型」的两个 template classes 互相操作,而类型检验(type checking)仍然起作用。

 
assignment(赋值)运算符的 template 版本并不会取代 default assignment 运算符。

 
你可以把 class templates 作为 template parameters 使用,称为 template template
parameters。

 
Template template arguments 必须完全匹配其对应参数。预设的 template arguments 会被编译
器忽略,要特别小心。

  当你实例化(instantiated)一个隶属内建类型(built-in
type)的变量时,如果打算为它设定初值,可明确调用其 default 构造函数。只有当你以 by value 方式使用字符串字面常数(string
literals)时,字符串底部的 array 才会被转 型(退化)为一个字符指针(也就是发生 "array-to-pointer"
转换)。

时间: 2024-10-24 02:37:42

template(4.1)的相关文章

NET Core项目定义Item Template

NET Core项目定义Item Template 作为这个星球上最强大的IDE,Visual Studio不仅仅提供了很多原生的特性,更重要的是它是一个可定制的IDE,比如自定义Project Template和Item Template就是一个非常有用的扩展点.这里我们主要关注Item Template,它时我们可以在"Add new Item"对话框中添加我们自定义的Item(如下图所示).如果不了解Item Template,Scott Gu的文章. 我们之前自定义了一些Ite

手把手教你创建Azure ARM Template

Azure的ARM模式在中国已经落地了.在ARM模式中,通过ARM的Template批量的创建各种资源是与ASM模式的最大的区别之一.目前Azure ARM的Template数量已经越来越多,更多的客户会选择采用Template的模式进行资源的部署: 在前面的文章中已经介绍了如何通过已有的Template修改成你所需要的模板,请参考: http://www.cnblogs.com/hengwei/p/5634380.html 本文将一步一步的创建一个最简单的存储账户的ARM Template,并

Backbone 模板 underscore template默认的转义符&lt;%= %&gt; 与jsp的冲

先定义转义符,因为默认的转义符<%= %> 与jsp的冲突(如果js模板写在jsp页面中)       _.templateSettings = { interpolate : /\{\{(.+?)\}\}/g }; 下面就可以这样写 <script type="text/template" id="detailedBar-template"> <div class='title'> <span class='label'&

ansible的playbook配置及template模板的使用

前言: 学习下ansible的playbooks的状态配置管理,说来puppet saltstack都有类似的叫法,只是ansible的叫法更犀利,我当时一看playbook还以为是花花公子的playboy.要使用ansible就要深入学习playbook配置及模板. 注:到底是playbook还是playbooks.... 先把官网的简单几个语法给说明下. #这个是你选择的主机 - hosts: webservers #这个是变量   vars:     http_port: 80     m

Spring mvc 中使用ftl引用共通文件出错 FreeMarker template error: Error reading included file &quot;/WEB-INF/ftl/common/errormessage.ftl&quot;

初次接触spring mvc,想做一个小的练习项目,结果在ftl文件中引用其它的共通ftl文件时出错. 目录结构如图所示,想在login.ftl中引用common下的errormessage.ftl <#include '/WEB-INF/ftl/common/errormessage.ftl' /> 结果画面报错: FreeMarker template error: Error reading included file "/WEB-INF/ftl/common/errormes

Spring Boot使用thymeleaf模板时报异常:template might not exist or might not be accessible by any of the configured Template Resolvers

错误如下: template might not exist or might not be accessible by any of the configured Template Resolvers 解决方法: 1.确定模板是否在默认templates文件夹里面,并且路径要和返回的View名字一致. 2.new ModelAndView("/log/loglist");这样写是不对的,应该把开头的斜杠去掉,改成:new ModelAndView("log/loglist&

HDOJ 1217 Floyed Template

1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <cstring> 5 #include<map> 6 using namespace std; 7 8 map<string,int>name; 9 const int INF = 1000000; 10 const int MAXSIZE = 1005; 11 const int

微信小程序 模板template的使用

一.文件目录结构如下: 二.在house页面使用这些模板: 三.在house页面引入样式 四.定义模板文件 五.总结: 1.在模板文件中必须以name="模板名称" 如: <template name="HeaderNavTemplate">......</template> 包裹: 2.在引入模板页面.wxml页面中 <import src="../template/tpl-headerNav/tpl-headerNav.w

设计模式的征途—17.模板方法(Template Method)模式

在现实生活中,很多事情都需要经过几个步骤才能完成,例如请客吃饭,无论吃什么,一般都包含:点单.吃东西.买单等几个步骤,通常情况下这几个步骤的次序是:点单=>吃东西=>买单.在这3个步骤中,点单和买单大同小异,最大的区别在于第2步-吃什么?吃面条和吃满汉全席可大不相同. 在软件开发中,有时候也会遇到类似的情况,某个方法的实现需要多个步骤(类似于“请客”),其中有些步骤是固定的,而有些步骤则存在可变性.为了提高代码复用性和系统灵活性,可以使用一种称之为模板方法模式的设计模式来对这类情况进行设计.

C++中template的简单用法

模板(Template)指C++程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计.C++ 的标准库提供许多有用的函数大多结合了模板的观念,如STL以及IO Stream.使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数.返回值取得任意类型. 一.函数模板 在c++入门中,很多人会接触swap(int&, int&)这样的函数类似代码如下: 1 void swap(int&a , int& b) { 2 int temp