17

我已经使用 GCC 4.8 测试了以下代码,因为我们无法形成对 void 的引用,所以无法编译。

#include <iterator>
int main()
{
    std::iterator_traits<void*> test;
}

这是否意味着 void* 不是迭代器?(意思是这里的概念)

编辑:

好的,问题不正确。我实际上要问的是为什么 C++ 需要这样的 void* 行为?是出于安全考虑,即防止人们写坏东西吗?

因为虽然延迟 void 是非法的,但指针算术是:

int main()
{
    std::uint8_t test[] = {1,2,3};
    void * wut = test;
    std::uint8_t * p2 = static_cast<std::uint8_t *>(wut + 1);
    std::cout << std::hex << static_cast<int>(*p2) << std::endl; 
}

因此,即使如您所说, void 没有大小,从 GCC 的角度来看,它确实如此。它是计算机中最小的可寻址单元的大小。

4

3 回答 3

31

任何迭代器类型都需要是可取消引用和可递增的,但您不能取消引用或递增void*。既然你链接cppreference.com了,它就从这里开始。


关于您更新的问题:出于安全原因。如果你想要一个指向内存中单个字节的指针,你可以使用char*unsigned char*或类似的东西。void*基本上只是一种存储地址的方法,它不应该用于访问任何东西。只有当您知道它指向什么时,您才应该将其转换为指向该类型的指针。

允许您添加(或减去)它的原因是 AFAIK 以实现向后兼容性。对于 avoid* p;你可以写p += 1;,但你不能++p;按照 per递增它

5.3.2 递增和递减[expr.pre.incr]

1 prefix 的操作数++通过添加来修改,如果是则1设置为(不推荐使用此用法)。操作数应为可修改的左值。操作数的类型应为算术类型或指向完全定义的对象类型的指针。truebool

(强调我的)。

于 2013-10-20T20:47:42.587 回答
13

为什么你不能有一个 void 类型的迭代器?

Per §3.9.1/9and §5.7, Typevoid不是完整类型,加法运算符不能应用于不完整指针类型:

void 类型有一组空值。void类型是不完整的类型,不能>完成...

...操作数是(应该是)指向完全定义对象的指针...

因此你不能有一个空洞的迭代器。

 

为什么你看到这个错误?

错误:形成对 void 的引用

声明std::iterator_traits,它在某处尝试声明对void您的情况的条目类型的引用。但是每§8.3.2/5 声明一个引用void是不合法的,因为你不能定义一个有效的对象void

应初始化引用以引用有效对象

于 2013-10-20T21:06:26.697 回答
7

答案很简单,因为您不能取消引用void*。void 也没有大小,所以你不能移动到下一个项目。迭代器类型必须是增量的和可取消引用的。

编辑:-

是出于安全考虑,即防止人们写坏东西吗?

是的,这是出于安全原因。你不能在你的编辑中做你试图做的事情。void*仅用于存储地址,如果您使用它来访问任何内容,则不允许使用它。

你可以wut = wut + 1void * wut

于 2013-10-20T20:48:19.913 回答