《C++ Templates: The Complete Guide》读书笔记

1、模板函数中,类型参数必须精确匹配,不支持自动(隐式)类型转换。

When we call a function template such as max() for some arguments, the template parameters are determined by the arguments we pass. If we pass two ints to the parameter type T const &, the C++ compiler must conclude that T must be int. Note that no automatic type conversion is allowed here. Each T must match exactly.

2、函数模板中,不允许指定默认模板参数类型(类模板中则可以)。

You may have as many template parameters as you like. However, in function templates(unlike class templates) no default template arguments can be specified.

【说明】:C++11标准已经允许函数指定默认模板参数类型了。

与函数参数默认值有一点不同,类模板的多个声明中,可以为不同的模板参数指定默认参数,不过必须满足条件:它之后的模板参数已经在之前的声明中指定默认参数。看8.2.4节中描述:

Similar to default function call arguments, a template parameter can have a default template argument only if default arguments were also supplied for the subsequent parameters. The subsequent default values are usually provided in the same template declaration, but they could also have been declared in a previous declaration of that template. (But default template arguments cannot be repeated.)

看下面一个例子比较形象:

template <typename T1, typename T2, typename T3, typename T4 = int>
class CTest; // ok, default argument for T4

template <typename T1, typename T2, typename T3 = short, typename T4>
class CTest; // ok, T4 already has default argument

// template <typename T1 = char, typename T2, typename T3, typename T4>
// class CTest; // error, T2 has no default argument

template <typename T1, typename T2, typename T3, typename T4>
class CTest
{ /* ...*/ };

int main()
{
    CTest<char, int> x; // equal to CTest<char, int, short, int> x;
    return 0;
}

3、模板函数中,当模板参数与函数参数之间没有任何关联时,编译器不能推导出模板参数类型,因而必须明确指定模板参数类型。

In case when there is no connection between template and call parameters and when template parameters cannot be determined, you must specify the template argument explicitly with the call.

例如:

template<typename Ret, typename T1, typename T2>
inline Ret max(const T1 &t1, const T2 &t2){ /**/ }
double x = max<double, int, double>(2, 3.3); // Ok,但是太冗长
double y = max<double>(2, 3.3);  // Ok,返回类型为double

4、非模板函数能够与同名的模板函数共存,即便模板函数能够被实例化为与非模板函数完全一样的参数类型,也不会报出编译错误,编译器会优先考虑调用非模板函数。

A nontemplate function can coexist with a function template that has same name and can be instantiated with the same type. All other factors being equal, the overload resolution process normally prefers this nontemplate over one generated from the template.

5、特化模板类

To specialize a class template, you have to declare the class with a leading template<> and a specification of the types for which the class template is spcecialized. The types are used as a template argument and must be specified directly following the name of the class.

6、偏特化模板类

模板类可以被部分特化,你可以在特殊情况下指定特殊实现,但是部分参数仍需用户指定。

Class templates can be partially specialized. You can specify special implementations for particular circumstances, but some template parameter must be still defined by the user.

7、非类型(nontype)模板参数的限制条件:

非类型模板参数必须是整数常量或拥有external linkage属性的指针。

Note that nontype template parameters carry some restrictions. In general, they may be constant integral values(include enumerations) or pointers to objects with external linkage.

8、模板模板参数(Template Template Parameters)

使用举例:

template <typename ElementType,
          template <typename /*T*/> ContainerType // because the template parameter (T) of the template template parameter is not used, you can omit its name
         >
class Container
{
              ContainerType<ElementType> container;
};

【说明】:书中说“Template template parameters for function templates are not allowed”,经测试g++编译器已经支持函数模板的模板参数为模板参数了。

9、模板代码常见的组织方式:

(1)、The Inclusion Model:模板的声明和实现放在同一个文件中或者通过#include将其实现包含进来。

(2)、Explicit Instantiation:模板函数、模板类及其成员函数和静态数据成员都可以显示实例化。

【说明】:模板类的明确实例化,将实例化该类的所有可实例化成员。

(3)、The Separation Model:该模型引入了export关键字,主流的编译器都不支持它,如g++、clang++直接忽略它。可参看《C++ 中 export 关键字的尴尬处境》(百度转载)。

10、成员函数模板不能声明为虚函数

常见的虚函数调用机制都采用一个固定大小的虚函数表,而成员函数模板实例化的个数在程序编译结束前无法确定。

Member function templates cannot be declared virtual. This constraint is imposed because the usual implementation of the virtual function call mechanism uses a fixed-size table with one entry per virtual function. However, the number of instantiations of a member function template is not fixed until the entire program has been translated. Hence, supporting virtual member function templates would require support for a whole new kind of mechanism in C++ compilers and linkers.

In contrast, the ordinary members of class templates can be virtual because their number is fixed when a class is instantiated.

11、实参(argument)与形参(parameter)的匹配

template <typename P> void fun1(P) {}
template <typename P> void fun2(P&) {}
int a[4];
int const ac = 2;
fun1(a);  // noreference parameter: P is int *,没有引用,int[4]类型decay成了指针类型
fun2(a);  // reference parameter: P is int[4],有引用,不能decay
fun1(ac); // noreference parameter: P is int,没有引用,忽略了top-level const
fun2(ac); // reference parameter: P is int const,有引用,保留top-level const
fun1(1);  // noreference parameter: P is int,没有引用,忽略了top-level const
fun2(1);  // reference parameter: P is int => Error: can't pass 1 to int &

We still need to explore how argument-parameter matching proceeds. We describe it in terms of matching a type A (derived from the argument type) to a parameterized type P (derived from the parameter declaration). If the parameter is declared with a reference declarator, P is taken to be the type referenced, and A is the type of the argument.

Otherwise, however, P is the declared parameter type, andA is obtained from the type of the argument by decaying array and function types to pointer types, ignoring top-level const and volatile qualifiers.

【说明】:(1) Decay is the term used to refer to the implicit conversion of function and array types to pointer types.

(2) The fact that no decay occurs for arguments bound to reference parameters can be surprising when the arguments are string literals. For example:

template<typename T>
T const& max(T const& a, T const& b);
max("apple", "banana");

It would be reasonable to expect that for the expression max("apple", "banana") T is deduced to be char const*. However, the type of "apple" is char const[6], and the type of "banana" is char const[7]. No array-to-pointer decay occurs (because the deduction involves reference parameters), and therefore T would have to be both char[6] and char[7] for deduction to succeed. That is of course impossible. (C++03标准文档2.13.4.1节中描述:“An ordinary string literal has type of "array of n const char" and static storage duration, where n is the size of the string as defined below, and is initialized with the given characters”,C++11标准文档2.14.5.8节中也有类似的描述)。

原文地址:https://www.cnblogs.com/opangle/p/2628310.html