标签:
最近发现了《Effective Modern C++》这本书,作者正是大名鼎鼎的Scott Meyers——《Effective C++》、《Effective STL》的作者。
而就在C++11逐渐普及,甚至是C++14的新特性也进入大家的视野的时候,《Effective Modern C++》一书应运而生。此书与其前辈一样,通过数十个条款来展开,只不过这次是集中于C++11和C++14的新特性。auto、decltype、move、lambda表达式……这些强而有力的新特性背后到底隐藏着哪些细节和要点?……
阅读这本书的时候,感受到的豁然开朗的愉悦与初学C++时看Scott前几本著作时别无二致。遂尝试摘录一二,结合所想,做些记录,同时也试着检查一些自己的知识点有哪些欠缺,希望大家能多多指正。
注意:
蛤蛤蛤蛤蛤
↑↑↑↑ 这样的方框里的片段完全不来自于原书,而是我自己的理解。
Item 1 的标题大概是翻译成“理解模板类型推导”。模板类型推导是C++长期以来的特性。比如:
template<typename T> void f(ParamType param); f(expr); // 调用 f
其中 ParamType 可以是和 T 有关的类型,只不过包含一些修饰,比如 const 或引用修饰符(reference qualifier)。如:
template<typename T> void f(const T& param); // ParamType 为 const T&
对于这样的调用:
int x = 0; f(x);
一个特化(Specialize)的函数就经由类型推导生成了,T 被推导(deduce)为 int,ParamType 则被推导为 const int& 。
上面这种过程是类型推导,而
template<> void f<int>(int);就不算类型推导了——因为并没有进行“类型推导”,而是直接指定了——cppreference上将这叫做instantiate,实例化。编译器将特化有特定模板参数的函数模板。
在这种形式中,T 的推导不仅依赖于 expr 的类型,还和 ParamType 的形式(form)有关。对此书中给出三种情形:
(此时书中并未详述什么叫universal引用,不过对此情形影响不大,因为universal引用首先就不是左值引用,即不是形如 int&、T&)
在第一种情形下,类型推导有如下规则:
上两条中,expr 指的就是函数的实参(argument),而 ParamType 是形参(parameter)的类型。书中例子为:
template<typename T> void f(T& param); int x = 27; const int cx = x; const int& rx = x; f(x); // T -> int, ParamType -> int& f(cx); // T -> const int, ParamType -> const int& f(rx); // T -> const int, ParamType -> const int&
expr,即上例的 x、cx、rx,去掉引用部分后为 int,const int,而 param 将要对这几种类型的变量建立引用,ParamType 就推导出了上述的结果。
其中很重要的一点:
当传递一个 const 对象到一个引用参数(parameter)时,调用者希望这个对象能保持 const 特性,即不变性。
模板类型推导遵从这一要点。故传递 const 对象到模板参数 T& 是安全的,不会丢失 const 属性。
并且以上规则对于右值引用也是成立的。
而将上例中的 ParamType 改为 const T& 时,上例三次调用全部将 ParamType 推导为 const int&,T 则每次都为 int。因为 ParamType 的形式中带有了 const,匹配后 T 就不需要带有 const 了。
而对于 ParamType 是指针的情形,推导过程也是同样的。只是去除了“忽略引用部分”这一步,只是对指针类型进行模式匹配。
模板函数的参数是universal引用的时候,比如“像是”右值(rvalue)引用,即 T&& 这样的类型,其中 T 的模板类型参数。
我想,所谓universal引用,可以先参考“引用叠加效果”表:
& & -> &
& && -> &
&& & -> &
&& && -> &&我想这可能念做:&引用的&引用是&引用,&引用的&&引用是&引用,&&引用的&引用是&引用……
我的理解是,其中任一种引用的&&引用都是原型,所以叫做universal引用吧。由于是叠加在未确定的模板类型 T 上的,所以写法虽然一样,但并不是右值引用,因为右值引用是作用于明确类型上的。
对于universal引用,类型推导规则为:
规则1可以对照引用叠加表,expr 的类型就是 -> 号右边的,如果它是左值即&,能通过universal引用变成这样状态的也只有左值&。即使 ParamType 被声明为和右值引用类似的形式,ParamType 本身也被推导为左值引用。
我认为这一推导正是由于待定的 ParamType 并不能表示一个右值引用类型,而只能作为一种“带有未知量 T”的类型运算表达式。比如 T 若是 int&&,则 T& 就是 int&。
书中对于情形2的例子为:
template<typename T> void f(T&& param); // param is now a universal reference
int x = 27; const int cx = x; const int& rx = x;
f(x); // x -> lvalue, T -> int&, ParamType -> int& f(cx); // cx -> lvalue, T -> const int&, ParamType -> const int& f(rx); // rx -> lvalue, T -> const int&, ParamType -> const int& f(27); // 27 -> rvalue, T -> int, ParamType -> int&&
第4个调用即为退回情形1规则的情况。expr 是一个右值,则进行模式匹配后被绑定到 int&&,其中 T 为 int。
就像这样,按值传递/按拷贝传递(pass-by-value):
template<typename T>
void f(T param);
那么 param 总是对实参(argument)进行拷贝。此情形有规则:
int x = 27; const int cx = x; const int& rx = x;
f(x); // T and ParamTypes -> int f(cx); // T and ParamTypes -> int f(rx); // T and ParamTypes -> int
因为 param 总是 expr 的拷贝,所以无论怎样都不会影响 expr,所以 expr 的 const、volatile 这些特性,都和 param 无关了。
这也正符合上面所说的,调用者希望传入的对象原本具有的特性(如 const)不受影响,程序的实现要遵从这一希望。
原书在这里举了一个 const char * const 按拷贝传递(按值传递——相对于按引用传递)的例子,不细表。
C/C++都有这样一个特性,那就是数组的退化(decay):
const char str[] = "hello"; // const char[6] const char *p = str; // 数组退化为指针
很明显 str 和 p 的类型是不同的。而且对于C中的语法,是可以将函数的参数声明为数组的形式的,但是以下两者却是相同的:
void func(char str[]); void func(char *str);
这是因为数组形式的形参(parameter),会被当作指针形式的形参处理。
因此对于按值传递的模板参数 T 来说,实参为数组 char[] 时,T 被推导为 char *。(可以认为数组的退化先发生。)
但模板参数为引用的时候,是能“真正”引用到传入的数组的(即不发生数组退化):
template<typename T> void f(T& param); f(str); // T -> const char[6], ParamType -> const char (&)[6]
一个例子,通过模板在编译期获取数组大小(代码中暂时无关的部分被去掉了):
template<typename T, std::size_t N> constexpr std::size_t arraySize(T (&)[N]) { return N; }
除了数组之外,函数也会退回为指针。但同时,同样能通过模板提供引用类型参数来避免退化:
void someFunc(int, double); // someFunc -> void(int, double)
template<typename T> void f1(T param);
template<typename T> void f2(T& param);
f1(someFunc); // ParamType -> void (*)(int, double) f2(someFunc); // ParamType -> void (&)(int, double)
数组和函数的退化都是针对其标识符。
Effective Modern C++ 读书笔记 Item 1
标签:
原文地址:http://www.cnblogs.com/chelxom/p/5755125.html