为什么不可能有一个指向void的引用?
为什么不可能有一个指向void的引用?
为什么不可能有一个指向void的引用?我在C++标准中找到的唯一一句话是在8.3.2.1处:\n引用到cv void类型的声明是非法的。\n为什么会这样?为什么我不能编写一个接受void引用的“通用”函数?\n只是为了明确一点,我没有任何有用的应用程序,使用引用到void可能比使用模板更好,但我只是对禁止这种结构的原理感到好奇。\n
\n为了进一步澄清,我了解到使用引用到void \"as is\"将是毫无意义的,就像解引用指向void的指针一样。然而,我可以将其强制转换为引用到某种类型,以便使用它,不是吗?事实上,我不明白为什么下面的代码片段可以工作...\n
void foo(void *data) { int *i = reinterpret_cast(data); // 对i进行一些操作 }
\n...而下面的代码片段却不行:\n
void foo(void &data) { int &i = reinterpret_cast(data); // 对i进行一些操作 }
为什么不可能有一个指向void的引用?
引用是指向某个实例的引用。
某个实例不能是void类型的。
任何一个实例都必须有一个特定的类型(可能还有基本类型)。
虽然你的陈述是正确的,但对于所提出的问题来说是无关紧要的,因为引用的类型不需要与其引用的事物的类型相同。例如,在“BaseClass& baseRef = derivedClassInstance;”中,引用的类型(一个基类)引用了另一个类型(一个派生类)的实例。虽然存在void引用并不意味着它们所引用的事物也具有void类型。这在void指针和它们指向的事物中显然不是这种情况。
你写道:“引用的类型不需要与它所引用的事物的类型相同...引用的类型(一个基类)引用了另一个类型(一个派生类)的实例” - 但我不同意,因为派生类型的实例也是基类类型的实例。
那么“long x; char& y = *(char*)&x;”呢?一般来说,引用只是意味着引用的事物应该被访问时“看起来”像是引用的类型。这并不保证被引用的内容实际上是给定类型的。只是编译器在解引用时可以假设它是给定类型。这与指针的类型(与它们指向的事物的类型)在解引用时的工作方式相同。
考虑到其他类型实际上是作为“void”的派生类型:就像普通的类继承一样,派生类型在“基本类型”void上添加了额外的操作,并且void指针可以指向任何没有类型转换的东西,并且可以向下转换为更具体类型的指针,如果要对其指向的内容进行操作。没有理由不将对void的引用视为相同的方式处理。显然,你不能有一个void的实例,但是你可以有一个引用void的引用,它引用了其他东西的实例,并且可以进行类似的向下转换。
除了在(已废弃的)C风格的内存函数(如malloc和free)中使用void*之外,你在哪里使用void*呢?用户 - 即具有或接收引用的任何代码 - 通常想知道某个东西的(类型)是什么;而能够声明不透明(前向定义)类型和模板类型提供了比void*更安全的类型选项。此外,你将引用视为指针类型,但我不确定这是否是它们的目的。
在C++中,我们可以使用`void*`来存储一个地址,但是忽略了具体的类型。虽然在这种情况下类型有点多余或者不太重要,但是我们仍然可以通过地址数据来操作它。但是对`void*`进行解引用操作是不合理的,因为无法确定要访问的成员,例如`p.mem`。我们无法确定要引用的类,从而无法确定要跳转到的内存和要跟随的虚函数表指针。
然而,仅仅引用`p`似乎是可以接受的,因为它只引用对象本身,而不引用其数据。这种操作不需要任何类信息,只需要地址。尽管我明白这样做没有任何用处,但是这也很重要,因为它定义了什么时候会出现问题。允许这种情况的话,一个C++的引用(常量解引用但不访问任何内容),例如`void& ref = static_cast< &void >(obj)`,也是有意义的,从而可以允许`void`引用。我并不是说大家应该向相关负责人提出这个问题,但是从“合理性”角度来看,这似乎是正确的,对吗?
正如Luc Touraille在上面指出的(至少我是这样解释的),这种操作是可以实现的,但是问题在于语义。我能想到的一个合理解释是,由于对象变量是一个内存序列的“标签”,类型对于语义是非常重要的。因此,指针被视为具有地址值的变量,类型有点多余 - 不是定义它的关键。
大多数人同意这个解释。我认为这是这个问题中最正确的答案。`void`表示“类型未知”,而不是“指向空”。指向空的是null,而不是void。你无法创建一个void的实例,因为它的大小未知,但是你可以有一个void的指针,因为虽然它的类型(因此大小)未知,但是它的地址仍然是已知的。拥有一个void的引用仍然是有意义的,尽管某些操作(如`sizeof()`)可能未定义或返回默认答案(例如“1”),因为C/C++不允许对象具有零大小。
我想要使用`void& something`来进行动态类型转换为其他类型。因此,一个void的引用可以保留`typeid`。就像每个虚类都是从`void`派生出来的一样。
为什么无法有一个指向void的引用?
如果您确实有一个指向void的引用,您会如何处理它呢?它不是一个数字、字符、指针或其他任何类型。您的假设的通用函数除了获取其地址(而不是其大小)之外,无法对其执行任何操作。
"void"有两个用途:声明对类型的任何了解(如void *),以及指定无任何东西而不是有某些东西(void函数返回)。在这两种情况下,除了它可能有一个地址之外,无法对void something说出任何信息。
如果您无法想出某种东西的有用途,我也无法想出,那至少可以证明某种东西是无用的,这可能至少是这里的部分原因。
实际上,有一件事情你可以用void引用做,只是语法上的问题:你可以传递对象而不解引用它,使用&,这样做很蠢,但我不喜欢在周围使用太多的&,我发现使用引用更清楚
我会用一个enforce_dlopen函数来使用void引用,它保证不返回空指针(与dlopen不同)。不幸的是,我无法在函数声明中表达这个事实。
我认为void引用有用:与void指针(可能是nullptr)不同,void引用将为您提供确保引用实际上引用某些东西的保证,即使您无法提供关于其类型的静态保证。
我实际上有一个可以使用这个功能的案例,但我还是在Google上搜索它,因为它可能是不可能的,因为它没有意义(几乎)。
对于构造函数来说,对void的引用有一个有用的属性,可以用作通用引用参数。因此,您可以有Foo a(anyReference),并且它可以在内部将其转换为void指针。它允许您传递引用,并且构造函数可以负责将其转换为指针。当然,这远非符合惯用的C++,但C++是一种多范式语言,在一个地方被认为是危险的东西在另一个地方是推动力。正如之前所指出的,构造函数中使用void引用比在构造函数中使用void指针更安全。
如果void&是无用的,就没有理由禁止它。标准是有意见的,并引入了一个不必要的例外。
我不明白这个回答如何回答问题。OP的假设函数为什么不起作用?本质上,它提供了与void*相同的功能,但增加了它不为空的确定性。
除了获取其地址之外,还有一些实际有用的事情可以做:您可以将其传递给dynamic_cast以将其转换为更有用的类型:MyType& foo = dynamic_cast<MyType&>(voidRef);
这相当于您可以使用void指针做的事情,但您知道void引用不为空(因为它是引用),如果您知道void引用应该是MyType的,您还知道如果尝试的类型转换是非法的,dynamic_cast将抛出异常(指针的dynamic_cast将返回null,然后您必须检查)。