std::pair<Key, Val> __tmp = *iter; // construct a temporary of the correct type
std::pair<Key, Val> const& item = __tmp; // then, take a reference to it
因为 auto 推导出初始化表达式的类型,所以不涉及类型转换 . 结合模板化算法,这意味着您可以获得比自己构成类型更直接的计算 - 特别是在处理类型无法命名的表达式时!
一个典型的例子来自(ab)使用 std::function :
std::function<bool(T, T)> cmp1 = std::bind(f, _2, 10, _1); // bad
auto cmp2 = std::bind(f, _2, 10, _1); // good
auto cmp3 = [](T a, T b){ return f(b, 10, a); }; // also good
std::stable_partition(begin(x), end(x), cmp?);
4 回答
auto
可以通过 avoiding silent implicit conversions 帮助表现 . 我发现一个令人信服的例子如下 .看到这个bug?我们在这里,以为我们正在复制 every 元素 . 这是因为
std::map<Key, Val>::value_type
是std::pair<const Key, Val>
,而不是std::pair<Key, Val>
. 因此,当我们(隐含地)具有:我们必须进行类型转换,而不是引用现有对象并将其留在那里 . 只要存在可用的隐式转换,就可以对不同类型的对象(或临时)进行const引用,例如:
类型转换是允许的隐式转换,原因与您将
const Key
转换为Key
的原因相同,但我们必须构造新类型的临时转换以便允许 . 因此,我们的循环有效:(当然,实际上并没有一个
__tmp
对象,它只是用于说明,实际上未命名的临时对象的生命周期仅限于item
) .只需改为:
刚刚保存了大量的副本 - 现在引用的类型与初始化程序类型匹配,因此不需要临时或转换,我们可以直接引用 .
因为
auto
推导出初始化表达式的类型,所以不涉及类型转换 . 结合模板化算法,这意味着您可以获得比自己构成类型更直接的计算 - 特别是在处理类型无法命名的表达式时!一个典型的例子来自(ab)使用
std::function
:使用
cmp2
和cmp3
,整个算法可以内联比较调用,而如果构造一个std::function
对象,不仅不能内联调用,而且还必须在函数的类型擦除内部中进行多态查找包装 .这个主题的另一个变体是你可以说:
这始终是一个引用,绑定到函数调用表达式的值,并且永远不会构造任何其他对象 . 如果你没有类型,你可能会被迫通过像
T && f = MakeAThing()
之类的东西构建一个新对象(可能是一个临时对象) . (此外,auto &&
甚至可以在返回类型不可移动且返回值为prvalue时工作 . )有两类 .
auto
可以避免类型擦除 . 有不可思议的类型(如lambdas)和几乎不可用的类型(如std::bind
或其他类似表达式的模板的结果) .如果没有
auto
,您最终必须将数据删除类似于std::function
. 类型擦除有成本 .task1
具有类型擦除开销 - 可能的堆分配,难以内联它,以及虚函数表调用开销 .task2
没有 . Lambdas需要自动或其他形式的类型扣除来存储而不需要类型擦除;其他类型可能非常复杂,以至于他们在实践中只需要它 .其次,你可以得到错误的类型 . 在某些情况下,错误的类型看似完美,但会产生副本 .
如果
expression()
返回Bar const&
或Bar
或甚至Bar&
,将编译,其中Foo
可以从Bar
构造 . 将创建临时Foo
,然后绑定到f
,其生命周期将延长,直到f
消失 .程序员可能意味着
Bar const& f
并且不打算在那里制作副本,但无论如何都要制作副本 .最常见的示例是
*std::map<A,B>::const_iterator
的类型,它是std::pair<A const, B> const&
而不是std::pair<A,B> const&
,但错误是一类错误,它们会无声地降低性能 . 您可以从std::pair<const A, B>
构造std::pair<A, B>
. ( Map 上的键是const,因为编辑它是一个坏主意)@Barry和@KerrekSB都在他们的答案中首次阐述了这两个原则 . 这只是尝试在一个答案中突出这两个问题,其中的措辞旨在解决问题而不是以实例为中心 .
现有的三个答案举例说明了使用
auto
帮助“makes it less likely to unintentionally pessimize”有效地使它成为"improve performance" .这枚硬币还有另一面 . 运用
auto
对于具有不返回基本对象的运算符的对象可能导致不正确(仍可编译和可运行)代码 . 例如,this question询问如何使用auto
使用特征库给出不同(不正确)的结果,即以下行导致不同的输出 . 不可否认,这主要是由于Eigens懒惰评估,但该代码对于(库)用户是透明的 .
虽然这里的性能没有受到太大影响,但使用
auto
来避免无意的悲观可能被归类为过早优化,或者至少是错误的;) .