当前位置: 首页 > 知识库问答 >
问题:

使用基于范围的C 11的正确方法是什么?

武成和
2023-03-14

对于,使用C 11基于范围的的正确方法是什么?

应该使用什么语法<代码>用于(自动元素:容器),或用于(自动


共有3个答案

罗和煦
2023-03-14

正确的方法总是

for(auto&& elem : container)

这将保证保留所有语义。

澹台展鹏
2023-03-14

对于(自动元素:容器)或(自动)没有正确的使用方法

让我详细说明一下。我们去散散步吧。

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;

}

正如评论所说,只是为了阅读。就是这样,只要使用得当,一切都是“正确的”。

伍溪叠
2023-03-14
匿名用户

>

  • 要观察元素,请使用以下语法:

    for (const auto& elem : container)    // capture by const reference
    

    >

  • 如果对象复制起来很便宜(如ints、Doubles等),可以使用稍微简化的形式:

      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 << ' ';
    

    上述代码打印向量中的元素(ints):

    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,其实现打包bools以优化空间(每个布尔值存储在一个位中,一个字节中有八个“布尔”位)。

    因此(因为不可能返回对单个位的引用),向量

    修改向量的元素

    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
    

    >

  • 如果对象复制起来很便宜(如ints、Doubles等),可以使用稍微简化的形式:

      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 。当您尝试在不存在的对象上调用方法时,抛出(发生)此异常。 以下是一些相关的切线:仔细阅读风险自负 不过要注意