什么是使用C ++ 11范围的正确方法
  什么是使用基于范围的C ++ 11的正确方式for ? 
  应该使用什么语法?  for (auto elem : container)还是for (auto& elem : container)或for (const auto& elem : container) ?  还是其他的? 
让我们开始区分大小写元素和修改它们之间的区别。
观察元素
我们来看一个简单的例子:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
    cout << x << ' ';
  上面的代码在vector打印元素( 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 (auto x : 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
  由于它可以从输出中读取, 复制构造函数调用是在基于范围的循环迭代过程中进行的。 
  这是因为我们通过值捕获容器中的元素( for (auto x : v) )中的auto x部分)。 
  这是低效率的代码,例如,如果这些元素是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 : 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 << ' ';
输出只是初始序列:
1 3 5 7 9
  相反,尝试使用for (const auto& x : v)只是无法编译。 
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
  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 << ' ';
输出是:
Hi Bob! Hi Jeff! Hi Connie!
代理迭代器的特例
  假设我们有一个vector<bool> ,并且我们想要使用上面的语法反转其元素的逻辑布尔状态: 
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::vector模板专门用于bool ,其中一个实现包装bool以优化空间(每个布尔值存储在一个位中,八个“布尔”位存储在一个字节中)。 
  由于这一点(因为不可能将引用返回到单个位), vector<bool>使用所谓的“代理迭代器”模式。  “代理迭代器”是一个迭代器,在取消引用时,不会产生普通的bool & ,而是(通过值)返回一个临时对象,它是一个可转换为bool的代理类。  (另请参阅StackOverflow上的这个问题和相关答案。) 
  要修改vector<bool>的元素,必须使用一种新的语法(使用auto&& ): 
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
  请注意for (auto&& elem : container)语法也适用于普通(非代理)迭代器的其他情况(例如,对于vector<int>或vector<string> )。 
  (作为一个方面说明,前面提到的for (const auto& elem : container) “观察”语法对于代理迭代器的情况也适用。) 
概要
上述讨论可以总结为以下指导原则:
要观察元素,请使用以下语法:
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<bool> ),请使用: 
for (auto&& elem : container)    // capture by &&
  当然,如果需要在循环体内创建元素的本地副本 ,则通过值( for (auto elem : container) ) 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)
要修改元素,请使用:
for (auto&& elem : container)
  没有正确的方法for (auto elem : container) ,或者for (auto& elem : container)或for (const 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(auto&& elem : container)
这将保证所有语义的保留。
链接地址: http://www.djcxy.com/p/72971.html上一篇: What is the correct way of using C++11's range
下一篇: Which C++ Standard Library wrapper functions do you use?
