我发现有时会使用 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>
不被认为是真正的容器,许多人认为它应该重命名(实现很好且有用,但应该有不同的名称,如 bitset
或 bitfield
或 dynamic_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; }
。 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/