Mr.*_*C64 182 c++ foreach c++11
使用C++ 11基于范围的正确方法是for什么?
应该使用什么语法?for (auto elem : container),for (auto& elem : container)或者for (const auto& elem : container)?还是其他一些?
Mr.*_*C64 337
让我们开始区分观察汇合点中的元素与修改它们的位置.
让我们考虑一个简单的例子:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
cout << x << ' ';
Run Code Online (Sandbox Code Playgroud)
上面的代码打印元件(int在S) vector:
Run Code Online (Sandbox Code Playgroud)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;
}
Run Code Online (Sandbox Code Playgroud)
如果我们在for (auto x : v) {...}这个新类中使用上面的语法:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (auto x : v)
{
cout << x << ' ';
}
Run Code Online (Sandbox Code Playgroud)
输出是这样的:
Run Code Online (Sandbox Code Playgroud)[... 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
由于可以从输出中读取,因此在基于范围的for循环迭代期间进行复制构造函数调用.
这是因为我们通过值
(部分)从容器中捕获元素.auto xfor (auto x : v)
这是低效的代码,例如,如果这些元素是实例std::string,可以完成堆内存分配,以及昂贵的内存管理器访问等.如果我们只想观察容器中的元素,这是没用的.
因此,可以使用更好的语法:通过const引用捕获,即const auto&:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (const auto& x : v)
{
cout << x << ' ';
}
Run Code Online (Sandbox Code Playgroud)
现在的输出是:
Run Code Online (Sandbox Code Playgroud)[... copy constructor calls for vector<X> initialization ...] Elements: 1 3 5 7 9
没有任何虚假(并且可能是昂贵的)复制构造函数调用.
所以,当观察到在一个容器(即用于只读访问)的元素,下面的语法是对简单低价到副本类型,如int,double等:
for (auto elem : container)
Run Code Online (Sandbox Code Playgroud)
另外,const在一般情况下,通过引用捕获更好,以避免无用(并且可能是昂贵的)复制构造函数调用:
for (const auto& elem : container)
Run Code Online (Sandbox Code Playgroud)
如果我们想要使用基于范围的方法修改容器中的元素for,则上述for (auto elem : container)和for (const auto& elem : container)
语法是错误的.
实际上,在前一种情况下,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 << ' ';
Run Code Online (Sandbox Code Playgroud)
输出只是初始序列:
Run Code Online (Sandbox Code Playgroud)1 3 5 7 9
相反,使用的尝试for (const auto& x : v)无法编译.
g ++输出如下错误信息:
Run Code Online (Sandbox Code Playgroud)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 << ' ';
Run Code Online (Sandbox Code Playgroud)
输出是(如预期的那样):
Run Code Online (Sandbox Code Playgroud)10 30 50 70 90
此for (auto& elem : container)语法也适用于更复杂的类型,例如考虑vector<string>:
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 << ' ';
Run Code Online (Sandbox Code Playgroud)
输出是:
Run Code Online (Sandbox Code Playgroud)Hi Bob! Hi Jeff! Hi Connie!
假设我们有一个vector<bool>,我们想要使用上面的语法反转其元素的逻辑布尔状态:
vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;
Run Code Online (Sandbox Code Playgroud)
上面的代码无法编译.
g ++输出类似于这样的错误消息:
Run Code Online (Sandbox Code Playgroud)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::vector模板专门为bool,与该一个实现包的bools到优化空间(每个布尔值被存储在一个比特,一个字节八"布尔"比特).
因此(因为不可能返回对单个位的引用),
vector<bool>使用所谓的"代理迭代器"模式.A"代理迭代器"是一个迭代的是,解除引用的时候,也不会产生一个普通的bool &,而是返回(由值)临时对象,它是一个代理类转化成bool.(另请参阅StackOverflow上的这个问题和相关答案.)
要在适当的位置修改元素vector<bool>,auto&&必须使用一种新的语法(使用):
for (auto&& x : v)
x = !x;
Run Code Online (Sandbox Code Playgroud)
以下代码工作正常:
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 << ' ';
Run Code Online (Sandbox Code Playgroud)
和输出:
Run Code Online (Sandbox Code Playgroud)false true true false
请注意,该for (auto&& elem : container)语法也适用于普通(非代理)迭代器的其他情况(例如,对于a vector<int>或a vector<string>).
(作为旁注,前面提到的"观察"语法for (const auto& elem : container)也适用于代理迭代器的情况.)
以上讨论可以概括为以下指南:
要观察元素,请使用以下语法:
for (const auto& elem : container) // capture by const reference
Run Code Online (Sandbox Code Playgroud)
如果对象的复制价格便宜(如ints,doubles等),则可以使用稍微简化的形式:
for (auto elem : container) // capture by value
Run Code Online (Sandbox Code Playgroud)
要修改元素,请使用:
for (auto& elem : container) // capture by (non-const) reference
Run Code Online (Sandbox Code Playgroud)
如果容器使用"代理迭代器"(如std::vector<bool>),请使用:
for (auto&& elem : container) // capture by &&
Run Code Online (Sandbox Code Playgroud)当然,如果需要在循环体内制作元素的局部副本,则通过value(for (auto elem : container))捕获是一个不错的选择.
在通用代码中,由于我们无法对泛型类型的假设T进行复制,因此在观察模式下,始终使用它是安全的for (const auto& elem : container).
(这不会触发潜在的昂贵的无用副本,对于廉价复制类型也会正常工作int,对于使用代理迭代器的容器也是如此std::vector<bool>.)
此外,在修改模式中,如果我们希望通用代码也能在代理迭代器的情况下工作,那么最好的选择是for (auto&& elem : container).
(对于使用普通非代理迭代器的容器,这也适用于std::vector<int>或者std::vector<string>.)
因此,在通用代码中,可以提供以下准则:
要观察元素,请使用:
for (const auto& elem : container)
Run Code Online (Sandbox Code Playgroud)要修改元素,请使用:
for (auto&& elem : container)
Run Code Online (Sandbox Code Playgroud)小智 14
没有正确的使用方法for (auto elem : container),for (auto& elem : container)或者for (const auto& elem : container).你只需表达你想要的东西.
让我详细说明一下.我们来散步吧.
for (auto elem : container) ...
Run Code Online (Sandbox Code Playgroud)
这个是语法糖:
for(auto it = container.begin(); it != container.end(); ++it) {
// Observe that this is a copy by value.
auto elem = *it;
}
Run Code Online (Sandbox Code Playgroud)
如果容器包含复制成本低的元素,则可以使用此方法.
for (auto& elem : container) ...
Run Code Online (Sandbox Code Playgroud)
这个是语法糖:
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;
}
Run Code Online (Sandbox Code Playgroud)
例如,当您想直接写入容器中的元素时,请使用此选项.
for (const auto& elem : container) ...
Run Code Online (Sandbox Code Playgroud)
这个是语法糖:
for(auto it = container.begin(); it != container.end(); ++it) {
// You just want to read stuff, no modification
const auto& elem = *it;
}
Run Code Online (Sandbox Code Playgroud)
正如评论所说,只是为了阅读.就是这样,如果使用得当,一切都是"正确的".
始终是正确的手段
for(auto&& elem : container)
Run Code Online (Sandbox Code Playgroud)
这将保证保留所有语义.
| 归档时间: |
|
| 查看次数: |
72329 次 |
| 最近记录: |