【发布时间】:2014-09-28 08:04:09
【问题描述】:
我发现有时使用for (auto& e : cont) 代替普通的for (auto e : cont)(其中cont 是某个容器,例如std::vector)。到目前为止,我已经找到了两个原因:
- 引用应该避免复制对象(更快的执行)
- 某些课程可能禁止复制(例如
std::thread)
经过几次测试,我可以看到:
-
for (auto& e : cont)适用于除std::vector<bool>之外的任何std::vector<T> -
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; }。 - 可以/应该更改 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)。 谢谢你:)
【问题讨论】:
-
您经常听到的准则是:
auto&&或auto const&,具体取决于您是否要修改元素。 -
typename iterator_traits<It>::reference r = *i; ++i; r = *i;使用这个迭代器会有奇怪的行为。我不确定它是否满足所有可变 RAIt 要求。 -
@firda 在这种情况下,您违反了
*i应返回reference而不是reference&的要求。 -
@firda 表 106 / [iterator.iterators] 在标准或更高版本的草案中。在 cppreference 上:en.cppreference.com/w/cpp/concept/Iterator 这些要求是从
Iterator“继承”到InputIterator。 -
这样的
static_assert可能出现在大量模板化的代码中,不是作为一种安全措施,而是为了改进错误消息(à la 概念)。重载解决方案仍然是我能想到的最佳答案,但我怀疑这是基本原理。也许要求太严格了。
标签: c++ c++11 boolean stdvector auto