它的公知的是std::vector<bool>
不满足标准的容器的要求,这主要是因为打包表示防止T* x = &v[i]
从一个指针返回到布尔。
我的问题是: 可以这样补救/缓解当reference_proxy重载地址的operator&
返回一个pointer_proxy?
指针代理可以包含相同的数据作为reference_proxy在大多数实现中,即一个指针到填充数据和掩模来隔离特定位内的块指向。 那么pointer_proxy的间接将产生reference_proxy。 本质上这两个代理是“胖”的指针,但是它们,还比较轻量相比,基于磁盘的代理容器。
代替T* x = &v[0]
一个可能然后执行auto x = &v[0]
并使用x
等if(*x)
没有问题。 我也想能够写for(auto b: v) { /* ... */ }
问题 :这样会与STL的算法多代理方式工作? 或者做一些算法真正依靠的是要求x
的需求是一个真正的bool*
? 或要求是有太多的连续用户定义的转换,以防止这个工作? 我想努力,全面完成上述实施草图前需要了解任何此类障碍物。
UPDATE(基于@HowardHinnant的答案,这个古老的讨论上comp.std.c ++)
你可以走了很长的路几乎模仿内建类型:对于任何给定类型T,一对代理(如reference_proxy和iterator_proxy)可以由在reference_proxy感::运算符&()和iterator_proxy相互一致::运算符* ()是彼此的逆。
然而,在某些时候一个需要映射代理对象后面的行为类似于T *或T&。 对于迭代器代理,可以重载operator - >(),并访问该模板T的接口,而重新实现所有功能。 然而,对于参考的代理,你需要重载operator(),而不是在当前的C ++允许(虽然塞巴斯蒂安·雷德尔提出了关于BoostCon 2013这样的建议 )。 你可以做一个详细的变通,如引用代理内获得()成员,或实现所有T的接口的参考内(这是什么矢量:: bit_reference完成),但这种要么失去了内置语法或引入不具备类型转换(你可以为每个参数最多一个用户自定义转换)内置语义用户定义的转换。
我的问题是:可以这样补救/缓解当reference_proxy重载运营商的地址的返回一个pointer_proxy?
的libc ++其实做到这一点。
#include <vector>
#include <cassert>
int main()
{
std::vector<bool> v(1);
std::vector<bool>::pointer pb = &v[0];
assert(*pb == false);
*pb = true;
assert(v[0] == true);
std::vector<bool>::const_pointer cbp = pb;
assert(*cbp == true);
v[0] = false;
assert(*cbp == false);
}
它甚至延伸到const_pointer
和const_reference
在模拟的相同类型的途径vector<int>
。 这是一个的libc不合格的扩展++。 但它使编写通用代码,这可能会在被实例化vector<bool>
更可能编译和正常运行。
问题:这样会与STL的算法多代理方式工作? 或者做一些算法真正依靠的是X需要一个真正的布尔*的要求? 或要求是有太多的连续用户定义的转换,以防止这个工作?
所有的的的libc ++算法一起工作vector<bool>
。 他们中有些人有相当骄人的业绩 。 一个特别的算法必须有特殊的待遇,其标准遗憾的是不强制要求:
#include <vector>
#include <cassert>
int main()
{
std::vector<bool> v(1);
bool b = true;
assert(v[0] == false);
assert(b == true);
std::swap(b, v[0]);
assert(v[0] == true);
assert(b == false);
}
这是很容易的实现来完成。 一个只需要确保swap
工程的任意组合bool
和vector<bool>::reference
。 但我不知道除了++的libc任何实现这样做,并且它不是由C ++ 11规定。
比特的阵列是精彩数据结构。 但不幸的是它在C ++标准规定不佳。 的libc ++已经有些无法无天的证明,这可能是一个非常有用的和高性能的数据结构。 希望的是,未来的C ++标准可以在这个方向上迁移到C ++程序员的利益。
随口说说我想说首先,它实际上更多地取决于每一个人的STL实现的细节,因为它不符合正式向A * reference_type是左值的T *的标准要求。 所以,关于潜在的执行问题:
其主要原因任何一段代码将是明确地依赖于容器的指针是实 bool*
是如果ALGO使用指针运算,在这种情况下,指针类型的大小变得相关。 指针运算虽然将绕过迭代器接口,从而战胜整个STL容器逐迭代器设计的主要目的。 的std ::矢量<>本身是保证连续的在C ++ 11,其允许最优化二者STL交易算法和编译器(:),这两者都可以在内部使用指针运算的特殊化。 如果你的类型不是自std :: vector的那么不应该是一个问题; 一切都应该只是假设迭代方法来代替。
然而! STL代码仍然可以采取指针不是指针运算的目的,而是用于其他目的。 在这种情况下,问题是C ++的语法 。 例如,引用您自己的问题:
代替T* x = &v[0]
一个可能然后执行auto x = &v[0]
在STL模板的任何代码也必须做同样的事情......而在这一点上,STL实现将作出广泛使用的似乎完全不可能的auto
。 可能还有其他的情况都在STL试图做到这一点失败告终,因为它不希望不匹配的引用类型聪明的r值铸造技巧。
关于for(auto b: v) { /* ... */ }
我看不出有什么理由不应该工作。 我认为它会生成代码,这将是比相同版本,你可以只推出自己在15分钟(或更少)的效率远不如。 既然你提到的OP,这imples一定的性能考虑内在我只把它。 你将不能够帮助它使用内部函数无论是。 没有什么固有的可以做,不知怎的,超过遍历位的排列顺序为一个简单的按位移位。 大部分的增加的开销将是从编译器生成的代码来更新迭代器指针和掩码值,然后重新上的下一次迭代的掩码值。 这将不能够神奇地演绎你正在试图做的,把它变成你一个顺序换档运什么。 它可以至少能够通过缓存入循环外的寄存器,优化了指针更新+回写阶段,但老实说,我会根据我的经验是非常怀疑。
下面是通过位会从开始到结束,只进行比较(能够开始在比特流将需要一些额外的设置逻辑任意点的版本)的缘故一种方式:
uint64_t* pBitSet = &v[-1]; // gets incremented on first iteration through loop.
uint64_t curBitSet = v[0];
for (int i=0; i<v.length(); ++i) {
if ((i % 64) == 0) {
curBitSet = *(++pBitSet);
}
int bit = curBitSet & 1;
curBitSet >>= 1;
// do stuff based on 'bit' here.
}