我发现有时会使用 for (auto& e : cont) 而不是普通的 for (auto e : cont) (其中 cont 是一些容器,例如 std::vector )。到目前为止,我发现了两个原因:

  • 引用应该避免复制对象(更快的执行)
  • 某些类可能禁止复制(例如 std::thread )

  • 经过几次测试,我可以看到:
  • for (auto& e : cont) 适用于除 std::vector<T>
  • 之外的任何 std::vector<bool>
  • for (cont::reference e : cont) 适用于任何 std::vector<T> 包括 std::vector<bool> (这里显而易见的问题是:
    我应该使用它而不是 for (auto& e : cont) 吗?
  • std::vector<bool> 不被认为是真正的容器,许多人认为它应该重命名(实现很好且有用,但应该有不同的名称,如 bitsetbitfielddynamic_bitset )
  • std::vector<bool> 可以以 for (auto& e : cont) 也能工作的方式实现(见下面我的尝试)

  • 这是我用于测试的代码:
    (诀窍是使用 reference& iterator::operator * () { return *this; } )
    #include <vector>
    #include <iostream>
    #include <typeinfo>
    using namespace std;
    
    #define USE_STD_VECT_BOOL 0
    
    #if USE_STD_VECT_BOOL
    typedef vector<bool> BITS;
    #else
    typedef class bvect {
        unsigned data; // we could use vector<unsigned> but this is just an examle
        unsigned size;
    public:
        bvect(): data(0), size(0) {}
        void push_back(bool value) {
            if(value) data |= (1u<<size);
            size++; }
        class reference {
            friend class bvect;
        protected:
            unsigned& data;
            unsigned  flag;
            reference(unsigned& data, unsigned flag)
            : data(data), flag(flag) {}
        public:
            operator bool() const {
                return data & flag; }
            reference& operator = (bool value) {
                if(value) data |= flag;
                else data &= ~flag;
                return *this; }
        };
        class iterator: protected reference  {
            friend class bvect;
            iterator(unsigned& data, unsigned flag)
            : reference(data, flag) {}
        public:
            typedef bool value_type;
            typedef bvect::reference reference;
            typedef input_iterator_tag iterator_category;
        //  HERE IS THE TRICK:
            reference& operator * () {
                return *this; }
            iterator& operator ++ () {
                flag <<= 1;
                return *this; }
            iterator operator ++ (int) {
                iterator tmp(*this);
                operator ++ ();
                return tmp; }
            bool operator == (const iterator& rhs) {
                return flag == rhs.flag; }
            bool operator != (const iterator& rhs) {
                return flag != rhs.flag; }
        };
        iterator begin() {
            return iterator(data, 1); }
        iterator end() {
            return iterator(data, 1<<size); }
    } BITS;
    #endif
    
    int main() {
        BITS bits;
        bits.push_back(0);
        bits.push_back(1);
    #if !USE_STD_VECT_BOOL
    //  won't compile for vector<bool>
        for(auto& a : bits)
            cout << typeid(a).name()
              << " = " << (int)(bool)a
              << endl;
    #endif
    //  std::_Bit_Reference
        for(BITS::reference a : bits)
            cout << typeid(a).name()
              << " = " << (int)(bool)a
              << endl;
    //  few more tests
        for(auto a : bits)
            cout << (int)(bool)a;
        for(bool a : bits)
            cout << (int)(bool)a;
        cout << endl;
    }
    

    问题:
  • 我应该使用 for (cont::reference e : cont) 而不是 for (auto& e : cont) 吗?
  • 这招有什么问题?它可以增强到适用于任何用例吗?
    编辑:我在这里指的是 bvect::reference& bvect::iterator::operator * () { return *this; }
  • 可以/应该更改 STL 吗? (引用 vector<bool> )

  • 反馈:答案和评论:
  • 使用 for (auto&& e : cont)(用于写作)或 for (const auto& e : cont)(用于阅读/枚举)似乎适用于所有情况。 (感谢 dyp 和 Praetorian)
  • 使用 typename iterator_traits<decltype(begin(cont))>::reference 似乎对数组也有效(cont=boo[2])。 (是的,它很丑,但我认为可以使用一些模板别名来缩短。我想不出需要这样做的反例,所以,现在,这不是解决方案。auto&& 是)
  • 标准说 iterator::operator * () 必须返回 iterator::reference (不是 iterator::reference& ),但仍然不知道为什么。

  • 最终判决:
    auto it = bits.begin();
    auto&& e = *it; cout << (bool)e;
    it++; cout << (bool)e;
    cout << endl;
    

    输出:
    10
    

    这绝对是坏事。我们应该坚持标准( iterator::operator * () 必须返回 iterator::reference )。谢谢 :)

    最佳答案

    vector<bool>vector 类模板的特化,它将 boolean 值存储在位域中以进行空间优化。由于您无法返回对位域的引用,因此 vector<bool>::reference 是一种类类型,代表单个 bool 的代理。 vector<bool>::operator[] 按值返回这个代理类实例;这同样适用于取消引用 vector<bool>::iterator

    vector<bool> cont;
    for (auto& e : cont) { ... }
    

    在这里,您试图将左值引用绑定(bind)到右值,这是不允许的。



    基于范围的 for 的好处是它也适用于普通的 C 数组。使用 cont::reference 将失败,以及任何没有名为 reference 的成员类型的可迭代类型。如果您希望对循环内的容器元素进行只读访问,则应使用 for(auto const& e : cont),如果要修改元素,则应使用 for(auto&& e : cont)

    在后一种情况下,auto&& e 是可以绑定(bind)到左值和右值的通用引用,因此它也适用于 vector<bool> 情况。

    关于c++ - 使用 `for(auto& e : cont)` 安全吗? vector<bool> 有什么问题?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25142557/

    10-11 10:46