在基于范围的 for 循环中使用转发引用的优点是什么?

如果我想执行只读操作,const auto&就足够了

for (auto&& e : v)  // v is non-const

这让我想知道:

有没有可能在某些模糊的角落情况下,与 auto&const auto&相比,使用转发引用有一些性能优势?

(shared_ptr是一个不起眼的角落案件的嫌疑人)


更新 我最喜欢的两个例子:

在迭代基本类型时使用常量引用有什么缺点吗?
我可以使用基于范围的 for 循环轻松地迭代映射的值吗?

请把注意力集中在问题上: 为什么我要使用自动 & & 在范围为循环?

33739 次浏览

我看到的唯一优势是当序列迭代器返回代理引用时,您需要以非常规方式操作该引用。例如:

#include <vector>


int main()
{
std::vector<bool> v(10);
for (auto& e : v)
e = true;
}

这不会编译,因为从 iterator返回的 rvalue vector<bool>::reference不会绑定到非常量 lvalue 引用。但这会奏效:

#include <vector>


int main()
{
std::vector<bool> v(10);
for (auto&& e : v)
e = true;
}

尽管如此,除非您知道您需要满足这样的用例,否则我不会这样编码。也就是说,我不会无缘无故这么做,因为这会让人们想知道你在做什么。如果我真的这么做了,不妨说明原因:

#include <vector>


int main()
{
std::vector<bool> v(10);
// using auto&& so that I can handle the rvalue reference
//   returned for the vector<bool> case
for (auto&& e : v)
e = true;
}

剪辑

我的最后一个案子应该是个有意义的模板。如果您知道循环总是处理代理引用,那么 auto将与 auto&&一样工作。但是,当循环有时处理非代理引用,有时处理代理引用时,那么我认为 auto&&将成为解决方案的选择。

使用 auto&&通用参考文献和基于范围的 for循环具有捕获所得内容的优势。对于大多数类型的迭代器,对于某种类型的 T,您可能会得到 T&T const&。一个有趣的例子是,对迭代器的解引用生成了一个临时值: C + + 2011得到了宽松的需求,迭代器不一定需要生成左值。通用引用的使用与 std::for_each()中的转发参数相匹配:

template <typename InIt, typename F>
F std::for_each(InIt it, InIt end, F f) {
for (; it != end; ++it) {
f(*it); // <---------------------- here
}
return f;
}

函数对象 f可以以不同的方式处理 T&T const&T。为什么一个基于范围的 for循环的主体应该是不同的?当然,要利用通用引用推断出类型的优势,你需要相应地传递它们:

for (auto&& x: range) {
f(std::forward<decltype(x)>(x));
}

当然,使用 std::forward()意味着您可以接受任何要从中移动的返回值。这样的对象在非模板代码中是否有意义我还不知道.我可以想象,使用通用引用可以为编译器提供更多的信息来做正确的事情。在模板化代码中,它不会决定对象应该发生什么。

我几乎总是使用 auto&&。为什么要在不必要的情况下被边缘病例咬伤呢?打字也更短,我只是觉得它更... 透明。当你使用 auto&& x,那么你知道 x正好是 *it,每次。