正确的方法总是
for(auto&& elem : container)
这将保证保留所有语义。
对于(自动元素:容器)或(自动)没有正确的使用方法
让我详细说明一下。我们去散散步吧。
for (auto elem : container) ...
这是语法糖:
for(auto it = container.begin(); it != container.end(); ++it) {
// Observe that this is a copy by value.
auto elem = *it;
}
如果您的容器中包含复制成本较低的元素,则可以使用此选项。
for (auto& elem : container) ...
这是语法糖:
for(auto it = container.begin(); it != container.end(); ++it) {
// Now you're directly modifying the elements
// because elem is an lvalue reference
auto& elem = *it;
}
例如,当您想直接写入容器中的元素时,请使用此选项。
for (const auto& elem : container) ...
这是语法糖:
for(auto it = container.begin(); it != container.end(); ++it) {
// You just want to read stuff, no modification
const auto& elem = *it;
}
正如评论所说,只是为了阅读。就是这样,只要使用得当,一切都是“正确的”。
>
要观察元素,请使用以下语法:
for (const auto& elem : container) // capture by const reference
>
如果对象复制起来很便宜(如int
s、Double
s等),可以使用稍微简化的形式:
for (auto elem : container) // capture by value
要在位修改图元,请使用:
for (auto& elem : container) // capture by (non-const) reference
>
如果容器使用“代理迭代器”(如std::vector
for (auto&& elem : container) // capture by &&
当然,如果需要在循环体中创建元素的本地副本,那么按值捕获(auto-elem:container)是一个不错的选择。
让我们开始区分观察容器中的元素和就地修改它们。
让我们考虑一个简单的例子:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
cout << x << ' ';
上述代码打印向量中的元素(int
s):
1 3 5 7 9
现在考虑另一种情况,在这种情况下,向量元素不仅是简单的整数,而且是更复杂类的实例,具有自定义的复制构造函数等。
// A sample test class, with custom copy semantics.
class X
{
public:
X()
: m_data(0)
{}
X(int data)
: m_data(data)
{}
~X()
{}
X(const X& other)
: m_data(other.m_data)
{ cout << "X copy ctor.\n"; }
X& operator=(const X& other)
{
m_data = other.m_data;
cout << "X copy assign.\n";
return *this;
}
int Get() const
{
return m_data;
}
private:
int m_data;
};
ostream& operator<<(ostream& os, const X& x)
{
os << x.Get();
return os;
}
如果我们对这个新类使用上面的for(autox: v){...}
语法:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (auto x : v)
{
cout << x << ' ';
}
输出类似于:
[... copy constructor calls for vector<X> initialization ...]
Elements:
X copy ctor.
1 X copy ctor.
3 X copy ctor.
5 X copy ctor.
7 X copy ctor.
9
由于可以从输出中读取,因此在基于范围的循环迭代期间进行复制构造函数调用。
这是因为我们按值从容器中捕获元素(autox
部分在for(autox: v)
中)。
这是低效的代码,例如,如果这些元素是std::string的实例,则可以进行堆内存分配,并需要昂贵的内存管理器访问等。如果我们只想观察容器中的元素,这是无用的。
因此,可以使用更好的语法:通过const
引用捕获,即const auto
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (const auto& x : v)
{
cout << x << ' ';
}
现在输出为:
[... copy constructor calls for vector<X> initialization ...]
Elements:
1 3 5 7 9
没有任何虚假(并且可能昂贵)的复制构造函数调用。
因此,当观察容器中的元素(即只读访问)时,以下语法对于简单的廉价复制类型很好,如int、double等:
for (auto elem : container)
否则,在一般情况下,通过const引用捕获更好,以避免无用的(并且可能昂贵的)复制构造函数调用:
for (const auto& elem : container)
如果我们想使用基于范围的
for修改容器中的元素,则上述for(auto elem:container)和for(const auto
事实上,在前一种情况下,elem存储原始元素的副本,因此对其所做的修改将丢失,而不会持久存储在容器中,例如:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v) // <-- capture by value (copy)
x *= 10; // <-- a local temporary copy ("x") is modified,
// *not* the original vector element.
for (auto x : v)
cout << x << ' ';
输出只是初始序列:
1 3 5 7 9
相反,尝试使用<代码>for(const auto
g输出类似这样的错误消息:
TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x'
x *= 10;
^
在这种情况下,正确的方法是通过非const
引用捕获:
vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
x *= 10;
for (auto x : v)
cout << x << ' ';
输出如下(如预期):
10 30 50 70 90
此<代码>用于(自动
vector<string> v = {"Bob", "Jeff", "Connie"};
// Modify elements in place: use "auto &"
for (auto& x : v)
x = "Hi " + x + "!";
// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
cout << x << ' ';
输出为:
Hi Bob! Hi Jeff! Hi Connie!
假设我们有一个向量
vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;
以上代码编译失败。
g输出类似以下内容的错误消息:
TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of
type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen
ce {aka std::_Bit_reference}'
for (auto& x : v)
^
问题是std::向量
模板专门用于bool
,其实现打包bool
s以优化空间(每个布尔值存储在一个位中,一个字节中有八个“布尔”位)。
因此(因为不可能返回对单个位的引用),向量
修改
向量的元素
for (auto&& x : v)
x = !x;
以下代码工作正常:
vector<bool> v = {true, false, false, true};
// Invert boolean status
for (auto&& x : v) // <-- note use of "auto&&" for proxy iterators
x = !x;
// Print new element values
cout << boolalpha;
for (const auto& x : v)
cout << x << ' ';
和输出:
false true true false
请注意(自动)的
(作为附注,前面提到的
的“观察”语法用于(const Auto
上述讨论可总结为以下指南:
>
要观察元素,请使用以下语法:
for (const auto& elem : container) // capture by const reference
>
如果对象复制起来很便宜(如
int
s、Double
s等),可以使用稍微简化的形式:
for (auto elem : container) // capture by value
要在位修改图元,请使用:
for (auto& elem : container) // capture by (non-const) reference
>
如果容器使用“代理迭代器”(如std::vector
for (auto&& elem : container) // capture by &&
当然,如果需要在循环体中创建元素的本地副本,那么按值捕获(auto-elem:container)是一个不错的选择。
在泛型代码中,由于我们无法假设泛型类型的复制成本很低,因此在观察模式下,始终使用(const auto
此外,在修改模式下,如果我们希望泛型代码在代理迭代器的情况下也能工作,那么最好的选择是(auto
因此,在通用代码中,可以提供以下准则:
>
for (const auto& elem : container)
要在位修改图元,请使用:
for (auto&& elem : container)
问题内容: 即使在使用Java Swing一年以上之后,对我来说,它仍然像魔术一样。如何正确使用BufferStrategy,尤其是方法? 我想添加一个JFrame和一个Canvas,然后进行绘制。我还希望能够调整()画布的大小。每次我调整Canvas的大小时,似乎都会被浪费掉,或者变得毫无用处,因为在上使用并没有真正做任何事情。另外,它具有怪异的不确定性行为,我不知道如何正确同步它。 这就是我的
问题内容: 我想在Linux上使用该机制。我希望我的应用程序知道何时更改了文件。能否请您提供给我一个示例,该怎么做? 问题答案: 文档(来自具有inotify的Monitor文件系统活动) 在C API 提供了三个系统调用来构建各种文件系统监视器: 在内核中创建子系统的实例,并在成功和失败时返回文件描述符。与其他系统调用一样,如果失败,请检查诊断。 顾名思义,它增加了一块 手表 。每个监视都必须提
问题内容: 需要帮助,以了解如何在UIKit中使用prepareForReuse()。该文件说 您只应重置与内容无关的单元格属性,例如Alpha,编辑和选择状态 但是如何重置单个属性属性(例如isHidden)呢? 假设我的单元格有2个标签,我应该在哪里重置: 标签文本 label.numberOfLines label.isHidden 我的tableView(_:cellForRowAt :)
一段时间以来,我一直试图将我的tableview工作作为一种电子表格,通过背景线程进行更新,当单元格更新时,它会亮起几秒钟(更改样式),然后返回到原始样式。我已经知道,我不能直接在表格单元格中存储和设置样式,我需要某种支持类来保存这些数据。但是tableview“重用”单元格(使用相同的单元格处理不同的数据)的行为真的很奇怪。当所有单元格都适合屏幕时,它对我来说完美无瑕,但一旦我放置大约100个单
那么,回到问题上来,鉴于微软建议不要在新的开发中使用WebRequest而是使用只提供异步API的HttpClient,我该怎么办呢? 这是我正在做的一些伪代码... 如何实现ProcessStuff()?
问题内容: 我正在与我的CompSci教授交谈,他建议将所有String 方法编写为: 而不是: 这两行都可以编译,但是我想知道第一种方法的好处是什么?我一直都是后一种方式。错了吗 什么是普通/常规? 问题答案: 第一种方法确保执行比较时不会收到 NullPointerException 。当您尝试在不存在的对象上调用方法时,抛出(发生)此异常。 以下是一些相关的切线:仔细阅读风险自负 不过要注意