C++11
标准推出了一个新的关键词auto
,这个关键词可以通过表达式自动推断返回值的类型,这也是新标准中被各编译器厂商支持最为广泛的特性之一。利用这个关键词可以有效减少代码的长度,特别是在使用模板元编程的时候。
举个简单的例子:
vector<map<int, string>> stringMapArray;
// 不使用auto版本 vector<map<int, string>>::iterator iter1 = stringMapArray.begin();
// 使用auto版本 auto iter2 = stringMapArray.begin(); |
看到这样简短的式子,我再也不能忍受第一个包含大量冗余信息的式子了。所以,最近写的C++里到处都是auto,恨不得在lambda的参数列表里也用(可惜不行)。
但是物极必反,auto的滥用却使一个非常隐蔽的问题悄然出现。最近写一个正则引擎的时候,发现运行效率总是低于预期,刚开始认为是动态内存分配的问题,通过替换成内存池后发现虽然效率有所提高,但是仍然达不到要求。于是想到了性能工具,一次检查下来发现如下语句竟然占用了70%的时间:
// 函数声明 set<int>& getSet();
void foo() { // 占用70%的时间 auto s = getSet();
... } |
检查发现原来auto
把函数的返回值推断成了set<int>
而不是set<int>&
这使得在赋值的时候发生了集合的复制,而这个集合包含了6万多个数据,这使得本应几乎不花时间的引用操作变成了耗时巨大的复制操作。
查了下C++标准 ISO/IEC 14882 Third edition P157,auto关键词的类型推断其实和模板的类型推断一致
§ 7.1.6.4 auto specifier
If the list of declarators contains more than one declarator, the type of each declared variable is determined as described above. If the type deduced for the template parameter U is not the same in each deduction, the program is ill-formed.
const auto &i = expr;
The type of
i
is the deduced type of the parameteru
in the callf(expr)
of the following invented function template:
template <class U> void f(const U& u);
所以之前的表达式相当于:
set<int>& getSet();
template <typename T> void foo(T t);
void bar() { // 这里的参数T推断成了set<int>,而不是set<int>& foo(getSet()); } |
如果我们想要获得引用类型,就要显式地写:
auto& refValue = getSet(); |
经过修改之后,这条语句的执行时间已经下降到忽略不计了。
这个教训告诉我们,使用auto
的时候虽然省事,但还是需要仔细考虑一下得到的类型到底是什么。