JavaScript is required to for searching.
跳过导航链接
退出打印视图
Oracle Solaris Studio 12.3:C++ 用户指南     Oracle Solaris Studio 12.3 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

第 1 部分C++ 编译器

1.  C++ 编译器

2.  使用 C++ 编译器

3.  使用 C++ 编译器选项

第 2 部分编写 C++ 程序

4.  语言扩展

5.  程序组织

6.  创建和使用模板

6.1 函数模板

6.1.1 函数模板声明

6.1.2 函数模板定义

6.1.3 函数模板用法

6.2 类模板

6.2.1 类模板声明

6.2.2 类模板定义

6.2.3 类模板成员定义

6.2.3.1 函数成员定义

6.2.3.2 静态数据成员定义

6.2.4 类模板的用法

6.3 模板实例化

6.3.1 隐式模板实例化

6.3.2 显式模板实例化

6.3.2.1 模板函数的显式实例化

6.3.2.2 模板类的显式实例化

6.3.2.3 模板类函数成员的显式实例化

6.3.2.4 模板类静态数据成员的显式实例

6.4 模板组合

6.5 缺省模板参数

6.6 模板专门化

6.6.1 模板专门化声明

6.6.2 模板专门化定义

6.6.3 模板专门化使用和实例化

6.6.4 部分专门化

6.7 模板问题部分

6.7.1 非本地名称解析和实例化

6.7.2 作为模板参数的本地类型

6.7.3 模板函数的友元声明

6.7.4 在模板定义内使用限定名称

6.7.5 嵌套模板名称

6.7.6 引用静态变量和静态函数

6.7.7 在同一目录中使用模板生成多个程序

7.  编译模板

8.  异常处理

9.  改善程序性能

10.  生成多线程程序

第 3 部分库

11.  使用库

12.  使用 C++ 标准库

13.  使用传统 iostream

14.  生成库

第 4 部分附录

A.  C++ 编译器选项

B.  Pragma

词汇表

索引

6.7 模板问题部分

本节介绍了使用模板时会遇到的问题。

6.7.1 非本地名称解析和实例化

有时模板定义使用模板参数或模板本身未定义的名称。如此,编译器解决了封闭模板作用域的名称,该模板可以在定义或实例化点的上下文中。名称可以在不同的位置具有不同的含义,产生不同的解析。

名称解析比较复杂。因此,您不应该依赖除一般全局环境中提供的名称外的非本地名称。也就是说,仅使用在任何地方都用相同方法声明和定义的非本地名称。在以下示例中,模板函数 converter 使用了非本地名称 intermediarytemporary。在 use1.ccuse2.cc 中这些名称的定义不同,因此在不同的编译器下可能会生成不同的结果。为了能可靠地使用模板,所有非本地名称(该示例中为 intermediarytemporary)在任何地方都必须有相同的定义。

use_common.h
// Common template definition
template <class Source, class Target>
Target converter(Source source)
       {temporary = (intermediary)source;
       return (Target)temporary;}
use1.cc
typedef int intermediary;
int temporary;

#include "use_common.h"
use2.cc
typedef double intermediary;
unsigned int temporary;

#include "use_common.h"

一个常见的非本地名称用法是在模板内使用 cincout 流。有时程序员要将流作为模板参数传递,这时就要引用到全局变量。但 cincout 在任何地方都必须有相同的定义。

6.7.2 作为模板参数的本地类型

模板实例化系统取决于类型名称,等效于决定哪些模板需要实例化或重新实例化。因此本地类型用作模板参数时,会导致严重的问题。小心在代码中也出现类似的问题。

示例 6-1 本地类型用作模板参数问题的示例

array.h
template <class Type> class Array {
        Type* data;
        int   size;
    public:
        Array(int sz);
        int GetSize();
};

array.cc
template <class Type> Array<Type>::Array(int sz)
    {size = sz; data = new Type[size];}
template <class Type> int Array<Type>::GetSize()
    {return size;}

file1.cc
#include "array.h"
struct Foo {int data;};
Array<Foo> File1Data(10);

file2.cc
#include "array.h"
struct Foo {double data;};
Array<Foo> File2Data(20);

file1.cc 中注册的 Foo 类型与在 file2.cc 中注册的 Foo 类型不同。以这种方法使用本地类型会出现错误和意外的结果。

6.7.3 模板函数的友元声明

模板在使用之前必须先声明。模板的使用由友元声明构成,不是由模板的声明构成。实际的模板声明必须在友元声明之前。例如,编译系统尝试链接以下示例中生成的目标文件时,对实例化的 operator<< 函数,会生成未定义错误。

示例 6-2 友元声明问题的示例

array.h
// generates undefined error for the operator<< function
#ifndef ARRAY_H
#define ARRAY_H
#include <iosfwd>

template<class T> class array {
    int size;
public:
    array();
    friend std::ostream&
        operator<<(std::ostream&, const array<T>&);
};
#endif

array.cc
#include <stdlib.h>
#include <iostream>

template<class T> array<T>::array() {size = 1024;}

template<class T>
std::ostream&
operator<<(std::ostream& out, const array<T>& rhs)
    {return out <<’[’ << rhs.size <<’]’;}

main.cc
#include <iostream>
#include "array.h"

int main()
{
    std::cout
      << "creating an array of int... " << std::flush;
    array<int> foo;
    std::cout << "done\n";
    std::cout << foo << std::endl;
    return 0;
}

请注意,因为编译器将以下行作为普通函数(array 类的 friend)的声明进行读取,所以编译期间不会出现错误消息。

friend ostream& operator<<(ostream&, const array<T>&);

因为 operator<< 实际上是模板函数,所以需要在声明 template class array 之前提供模板声明。但是,由于 operator<< 有一个 array<T> 类型的参数,因此必须在声明函数之前声明 array<T>。文件 array.h 必须如此示例所示:

#ifndef ARRAY_H
#define ARRAY_H
#include <iosfwd>

// the next two lines declare operator<< as a template function
template<class T> class array;
template<class T>
    std::ostream& operator<<(std::ostream&, const array<T>&);

template<class T> class array {
    int size;
public:
    array();
    friend std::ostream&
      operator<< <T> (std::ostream&, const array<T>&);
};
#endif

6.7.4 在模板定义内使用限定名称

C++ 标准要求使用具有限定名的类型,这些限定名取决于要用 typename 关键字显式标注为类型名称的模板参数。即使编译器可以推断出它应当为某一类型,也需满足该要求。以下示例中的注释说明了具有要用 typename 关键字的限定名的类型。

struct simple {
  typedef int a_type;
  static int a_datum;
};
int simple::a_datum = 0; // not a type
template <class T> struct parametric {
  typedef T a_type;
  static T a_datum;
};
template <class T> T parametric<T>::a_datum = 0;   // not a type
template <class T> struct example {
  static typename T::a_type variable1;             // dependent
  static typename parametric<T>::a_type variable2; // dependent
  static simple::a_type variable3;                 // not dependent
};
template <class T> typename T::a_type             // dependent
  example<T>::variable1 = 0;                      // not a type
template <class T> typename parametric<T>::a_type // dependent
  example<T>::variable2 = 0;                      // not a type
template <class T> simple::a_type   // not dependent
example<T>::variable3 = 0;          // not a type

6.7.5 嵌套模板名称

由于 ">>" 字符序列解释为右移运算符,因此在一个模板名称中使用另一个模板名称时必须小心。确保相邻的 ">" 字符之间至少有一个空格。

例如,以下是形式错误的语句:

Array<String<10>> short_string_array(100); // >> = right-shift

被解释为:

Array<String<10 >> short_string_array(100);

正确的语法为:

Array<String<10> > short_string_array(100);

6.7.6 引用静态变量和静态函数

在模板定义中,编译器不支持引用在全局作用域或名称空间中声明为静态的对象或函数。如果生成了多个实例,由于每个实例引用了不同的对象,因此违背了单次定义规则(C++ 标准的 3.2 节)。通常的失败指示是链接时丢失符号。

如果想要所有模板实例化共享单一对象,那么请使对象成为已命名空间的非静态成员。如果想要模板类的每个实例化不同对象,那么请使对象成为模板类的静态成员。如果希望每个模板函数实例化的对象不同,请使对象成为函数的本地对象。

6.7.7 在同一目录中使用模板生成多个程序

如果要通过指定 -instances=extern 生成多个程序或库,请在不同的目录中生成这些程序或库。如果要在同一目录中生成多个程序,则在各次生成之间需要清除系统信息库。该做法可以避免出现任何不可预料的错误。有关更多信息,请参见7.4.4 共享模板系统信息库

考虑以下包含 makefile a.cc b.ccx.hx.cc. 的示例请注意,仅当指定了 -instances=extern 时,该示例才有意义:

........
Makefile
........
CCC = CC

all: a b

a:
    $(CCC) -I. -instances=extern -c a.cc
    $(CCC) -instances=extern -o a a.o

b:
    $(CCC) -I. -instances=extern -c b.cc
    $(CCC) -instances=extern -o b b.o

clean:
    /bin/rm -rf SunWS_cache *.o a b
...
x.h
...
template <class T> class X {
public:
  int open();
  int create();
  static int variable;
};
...
x.cc
...
template <class T> int X<T>::create() {
  return variable;
}

template <class T> int X<T>::open() {
  return variable;
}

template <class T> int X<T>::variable = 1;
...
a.cc
...
#include "x.h"

int main()
{
  X<int> temp1;

  temp1.open();
  temp1.create();
}
...
b.cc
...
#include "x.h"

int main()
{
  X<int> temp1;

  temp1.create();
}

如果同时生成 ab,请在两个生成之间添加 make clean。以下命令会引起错误:

example% make a
example% make b

以下命令不会产生任何错误:

example% make a
example% make clean
example% make b