QA式语言迁移学习
from Python3 to C++17
本文用Question-Answer这种一问一答的方式,让一个熟悉Python3开发的人,快速上手C++17,找到相同思想在新语言中的最佳实践 ( best practice ),以及新特性下需要避免的写法。
这里我们用Python和C++做个例子,相对熟悉的两种语言。
Q: C++是动态类型,我需要标注每一个变量类型吗?
不一定。使用auto和decltype。C++需要每一个变量在编译期有确定的类型(除了void*)。如果编译器在上下文能推断出类型,用auto类型就可以了。
auto a =3;
decltype(e)可以取变量e的类型,用于填写模版或声明变量和函数。
decltype(auto)是C++14新增的类型指示符,可以用来声明变量以及指示函数返回类型。
当decltype(auto)被用于声明变量时,该变量必须立即初始化。假设该变量的初始化表达式为e,那么该变量的类型将被推导为decltype(e)。也就是说在推导变量类型时,先用初始化表达式替换decltype(auto)当中的auto,然后再根据decltype的语法规则来确定变量的类型。
decltype(auto)也可以被用于指示函数的返回值类型。假设函数返回表达式e,那么该函数的返回值类型将被推导为decltype(e)。也就是说在推导函数返回值类型时,先用返回值表达式替换decltype(auto)当中的auto,然后再根据decltype的语法规则来确定函数返回值的类型。
Q: 我需要为每一个类型写一个函数么,重复工作啊!以及,如何判断参数类型以执行分支,像isinstance(obj,class)一样?
Python的变量是一个名字(标识符,identifier),而C++的变量同时有名字和类型。类型是函数签名的一部分,不同的签名是不同的函数。C++的函数重载(Python没有函数重载)和函数模板在编译时批量生成"同名函数",并根据传参类型调用具体的一个;虚函数、继承和dynamic_cast则在运行时选择同名函数。
或者,使用宏,在编译前选择函数或类型。
基于模板和函数重载的:
- decltype可以取变量的类型,用于填写模版。
- 模版可以接受类型做为参数,根据实际出现的函数调用,填写"模板"生成一组同名函数。
- 折叠表达式…可以让模板生成参数量可变的函数。
- 函数重载(Python没有的特性)允许依据参数类型调用不同函数。模板相当于自动生成的重载函数的机制。
基于继承树的:
- 基类虚函数。从基类指针调用成员函数时,会根据实际指向的基类或子类值,调用基类或子类的同名函数。
- dynamic_cast在运行时尝试从基类指针(引用)转化为子类的。有失败的可能。额外的性能代价。
基于宏的:
- 条件编译:在编译前根据宏替换一些字符串。这样,程序中的变量的实际类型可以变化。比如,int32和int64。
Q: 容器类型在哪里,List, Dict 和Tuple?
C++的STL有容器类(container),配合迭代器(iterator,比Python里的iterator… … 更严肃?)和算法(Algorithm) 使用。以下tuple,string,get,tie,ignore,tuple_element均在命名空间std里,using namespace std;
。
std::vector
同类元素,数字索引,长度自动增长。
vector<int> vec {1,2,3};
vec.push_back(4);
std::map<T_key,_value>
map<string,int>d {
{"a":1},
{"b":2}
};
d["c"]=3;
std::tuple<T…>
异类元素,定长。
typdef tuple<int,float,string> MyTuple;
MyTuple t {1,1.0,"vvv"};
cout<<get<0>(t);//1
int a;
string c;
float b = get<float>(t);
tuple_element<0,MyTuple>::type b2 = get<1>(t);
int tuple_length = tuple_size<MyTuple>::value; // 3
Q: 那么元组拆包呢?用作函数调用?
用tie和ignore做元组拆包
tuple<int,float,string> t {1,1.1,"aaa"};
int a;
string c;
tie(a,ignore,c) = t;
tuple_cat(t0,t1)拼接元组
forward_as_tuple(T…)组装forward元组
从参数建立一个包含引用的元组。If the arguments are temporaries, forward_as_tuple
does not extend their lifetime; they have to be used before the end of the full expression.
尤其用于配合apply函数传参,forward会保留rvalue属性。
apply用元组做参数调用函数
The tuple need not be std::tuple
, and instead may be anything that supports std::get
and std::tuple_size
; in particular, std::array
and std::pair
may be used.