为什么无法引用void?我在C ++标准中找到的唯一内容是 8.3.2.1
指定类型“ cv void的引用”的声明符是不正确的。
为什么会那样?为什么我不能写一个接受void&
的“通用”函数?
为了清楚起见,我没有任何有用的应用程序,使用引用void可能比使用模板更好,但我只是好奇禁止这个结构的基本原理。
为了澄清一点,我理解使用“按原样引用”将与取消引用指向void的指针一样毫无意义。但是,为了使用它,我可以将它转换为引用到 sometype ,不是吗?事实上,我不明白为什么以下代码片段可以正常工作......
void foo(void *data)
{
int *i = reinterpret_cast<int*>(data);
// do something with i
}
......虽然这个不能:
void foo(void &data)
{
int &i = reinterpret_cast<int&>(data);
// do something with i
}
答案 0 :(得分:37)
如果您确实提到了void,那么您会怎么做?它不是数字,字符,指针或类似的东西。您的假设通用函数无法对其执行任何操作,除了获取其地址(而不是其大小)。
“void”有两个用途:放弃任何类型的知识(如void *),并指定任何东西而不是某些东西(void函数返回)。在任何一种情况下都不可能对某个虚空说些什么,除了它可能有一个地址。
如果你想不出某种方式可以有用,而我不能,那至少证明某些东西是无用的,而且这可能至少是这里理论的一部分。
答案 1 :(得分:14)
首先问问你自己,你将如何取消引用无效指针?
void *p = /*something*/ ;
cout << *p << endl;
上面的代码没有意义,我们无效的原因之一是我们可以说“我需要在这里做一些通用指针,我既不知道也不关心我指向的东西”。 根据定义,编译器不知道void *指向什么,因此它不能取消引用它。你可以 - 通过强制转换 - 但编译器不能。
对void的引用会受到同一问题的影响,根据定义,指向的数据没有类型,因此无法以任何有意义的方式引用它。
要引用它 - 程序员 - 需要将其转换为其他类型,然后您可以对其进行类型化引用。
不确定我是否也按照我的想法解释了这一点。
鲁本,有什么想法吗?编辑:回答您的修改。
获取第一个函数,传递void * data。 数据是一个完全有效的项目,您可以使用它进行计算,或者如果您实施了一些日志记录,则可以记录它。
logger << data;
您将获得地址数据指向。如果您尝试取消引用数据,编译器将给您一个错误(暂时没有C ++编译器,所以不确定实际错误)。 例如
void* data = /* some assignment */;
logger << *data; // compiler error.
现在,编译器不会因为任何原因而让你取消引用void *(它没有意义),同样代表对void&amp; data的引用,除了因为它是一个引用它的隐式取消引用。编译器不允许您在一次操作中取消引用void *,它不会让您不断地取消引用它。
void& data = /* some assignment *.;
logger << data; // means same as logger << *data above
你不能 ANTHTHING 数据 EXCEPT 取得它的地址,并且有一个完美的 - 安全 - 方法内置到语言中,即< / p>
void* data;
这有什么意义吗?
答案 2 :(得分:6)
引用是对某个实例的引用。
某事物的实例不能是void
类型。
任何事物的实例都必须具有特定类型(可能还有基类型)。
答案 3 :(得分:3)
以下是我所说过的不同事情的总结。
1 他们本来就没用了。
实际上,如果我们回顾一下C的时代,void指针有两个目的:
当C ++问世时,模板成为实现通用性的最佳解决方案。但是,自定义内存管理仍然是必须的,C ++和C之间的互操作性是一个主要问题,所以void *得到了保证。假设的无效引用对内存管理没有帮助,并且已经涵盖了通用性,所以基本上几乎没有用(除了下面描述的非null的保证)。
2 你无法用它做任何事情
使用void指针时,不允许取消引用它;转换为引用的情况,这意味着您不能使用(总是假设的)void引用。所以
void *data = // something
// using *data and data-> is forbidden
void &data = // something
// using data is forbidden
但是,我们可以想到一个用例,其中引用不必被“取消引用”(这个短语非常不正确,但你得到了我的观点),但我们只会采用它的地址。我们假设我有以下功能:
void foo(void *dataptr)
{
assert(dataptr != NULL); // or != 0
// do something with dataptr
}
为了避免这种烦人的断言,我可以用这种方式编写函数:
void foo(void &dataref)
{
void *data = &dataref;
// do something with data
}
但是,为了实现这一目标,&dataref
需要等同于dataptr
,不等于:&dataref
相当于{{1} }}!
因此,即使采用地址也意味着解除引用,至少在概念上(在幕后,第一个等价可能是真的,但在语义层面却不是)。因此,我们绝对没有使用数据,因此无效引用是一种失常。
答案 4 :(得分:2)
从技术上讲,所有保证的是对对象的引用是它的别名。在引擎下引用参数传递是通过指针完成的,是一个实现细节。这可能令人困惑,因为重复使用&amp;运算符也是地址的,但请记住,运算符实际上在不同的上下文中具有不同的含义(在变量或参数声明中,它表示引用类型,否则它的地址为,除非它是按位的 - )。因为它在技术上只是对象的别名,所以引用“总是被解除引用”,正如Worrier解释的那样。
答案 5 :(得分:1)
void*
的想法是你仍然有一个包含地址的有效变量,但是这个类型被忽略了。这似乎是允许的,因为我们仍然可以处理地址数据 - 在这种情况下,类型有点多余(或不太重要)。取消引用它是不好的,因为尝试访问成员没有意义,例如p.mem
。我们不知道要引用哪个类,因此要跳转到的内存,要遵循的vtable指针。
然而,似乎有意义的是p
本身就可以,因为它只引用了对象,但没有引用它的数据。没有类信息需要这样做,只需要地址。我知道这绝对没用,但是在确定事情何时发生故障时这很重要。允许这个概念,一个C ++引用(经常被解除引用但不访问任何东西),例如void& ref = static_cast< &void >(obj)
也有意义,因此允许无效引用。我并不是说任何人都应该与负责人讨论,但从“有道理”的角度来看,这似乎是正确的,不是吗?
正如Luc Touraille上面指出的那样(至少,这是我的解释),它可以实现,但问题是语义问题。我可以得出的合理解释是,由于对象变量是一系列内存的“标签”,因此该类型具有重要的语义价值。因此,指针被认为是具有地址值的变量,将类型视为有点多余 - 不是定义它的关键。
有人会同意吗?
答案 6 :(得分:0)
您可以将引用视为取消引用的指针。从语法上讲,您将引用视为不是指针:您不需要*运算符来取消引用它,您可以使用。而不是 - &gt;访问其成员。
但是,您无法取消引用void
指针。正如Binary Worrier所指出的,尝试这样做会给你一个编译器错误。如果你没有一个解除引用的void指针,那就意味着你不能有一个无效的引用。
答案 7 :(得分:0)
如果它们是,那么它们在语义上与指针没有区别,并且相当于语法糖。一篇参考文献说,“我指的是这种类型的东西。”允许void或null引用会削弱与指针的差异。
当然,引用仍然可以引用不再存在的对象,但这是一个例外。
答案 8 :(得分:0)
以下不是对void引用概念的辩护。我把它作为狂野的轶事提供。问问自己它是否闻起来很有趣。
我的公司是第一个使用C ++商业化的公司之一,最初是使用Cfront编译的。早期的开发人员仍然在学习这门语言,并且通常使用书中的每一个技巧(各地的运营商!)。这是他们认为很酷的技巧:
void Foo::something(int action, ostream &os = *(ostream *)0)
{
ostream *os_p = &os;
if (&os == (ostream *)0) {
os_p = &cerr;
}
// continue with method
}
所以这里有一个,而不是一个void引用,而是一个带有潜在 void 绑定的类型化引用!片刻的想法应该可以提出更好的替代方案......
答案 9 :(得分:-2)
void 是不存在的,因此拥有它的地址是不合逻辑的。