49

为什么不可能引用 void?我在 C++ 标准中找到的唯一内容是这一行,位于8.3.2.1

指定类型“对cv void的引用”的声明符格式不正确。

为什么会这样?为什么我不能编写一个接受 a 的“通用”函数void&

为了清楚起见,我没有想到使用 void 引用可能比使用模板更好的有用应用程序,但我只是对禁止这种构造的理由感到好奇。


为了澄清一点,我知道“按原样”使用对 void 的引用与取消引用指向 void 的指针一样毫无意义。但是,我可以将它转换为对某种类型的引用以便使用它,不是吗?事实上,我不明白为什么下面的代码片段可以工作......

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
}
4

10 回答 10

39

如果你确实提到了 void,你会用它做什么?它不会是数字、字符、指针或类似的东西。您假设的通用函数无法对其执行任何操作,除了获取其地址(而不是其大小)。

“void”有两个用途:否认任何类型知识(如在 void * 中),以及不指定任何东西而不是某物(void 函数返回)。在这两种情况下,除了它可能有一个地址之外,不可能说任何关于 void 的东西。

如果你想不出某种有用的方法,而我想不出,那至少是某物无用的证据,这很可能至少是这里的部​​分理由。

于 2009-01-19T15:26:16.957 回答
15

首先问问你自己,你将如何取消引用一个 void 指针?

void *p = /*something*/ ;
cout << *p << endl;

上面的代码是没有意义的,我们有 void 的原因之一是我们可以说“我需要在这里做一些通用的指针工作,我既不知道也不关心我指向什么”。根据定义,编译器不知道 void * 指向什么,因此它不能取消引用它。您可以 - 通过强制转换 - 但编译器不能。

对 void 的引用也存在同样的问题,根据定义,指向的数据没有类型,因此不能以任何有意义的方式引用它。

要引用它,您 - 程序员 - 需要将其转换为另一种类型,然后您可以对它进行类型化引用。

不确定我是否按照我的意愿解释了这一点。

鲁本,有什么想法吗?

编辑:回答您的编辑。

使用第一个函数,传递 void* 数据。data 是一个完全有效的项目,你可以用它来计算,或者如果你实现了一些日志记录,你可以记录它。

logger << data;

你会得到地址数据点。如果您尝试取消引用数据,编译器会给您一个错误(目前没有方便的 C++ 编译器,因此不确定实际错误)。例如

void* data = /* some assignment */;
logger << *data; // compiler error.

现在,编译器不会让您出于任何原因取消引用 void* (这没有意义),同样代表对 void &data 的引用,只是因为它是引用,所以一直隐式取消引用。编译器不会让您在一个操作上取消引用 void*,它不会让您不断地取消引用它。

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

除了获取地址之外,您无法对数据做任何事情,并且语言中内置了一种非常好的 - 且安全的方法来做到这一点,即

void* data;

这更有意义吗?

于 2009-01-19T15:18:09.937 回答
6

引用是对某事物实例的引用。某事的实例不能是 type void。任何事物的实例都必须具有特定的类型(可能还有基本类型)。

于 2009-01-19T15:15:31.440 回答
3

以下是我已经说过的和我想到的不同事情的摘要。

不允许引用 void 的两个主要原因


1他们完全没用。

事实上,如果我们回顾 C 的时代,空指针有两个用途:

  • 内存管理(例如 malloc)
  • 通用性(编写可以接受任何类型参数的函数)

当 C++ 出现时,模板成为实现泛型的最佳解决方案。然而,自定义内存管理仍然是可能的,并且 C++ 和 C 之间的互操作性是一个主要问题,因此保留了 void*。假设的 void 引用对内存管理没有帮助,并且已经涵盖了通用性,因此基本上它几乎没有用处(除了下面描述的非空性保证)。

2你将无法用它做任何事情

使用 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等效于&*dataptr!

因此,即使取地址也意味着取消引用,至少在概念上是这样(在幕后,第一个等价可能是真的,但在语义层面上却不是)。因此,我们绝对不能使用数据,所以空引用是一种异常。

于 2009-01-19T17:38:05.553 回答
2

从技术上讲,所有可以保证的是对对象的引用是它的别名。引擎盖下的引用参数传递是通过指针完成的,这是一个实现细节。这可能会令人困惑,因为引用重用了也是地址的 & 运算符,但请记住,该运算符实际上在不同的上下文中具有不同的含义(在变量或参数声明中,它表示引用类型,否则它是地址,除非它是按位与)。因为它在技术上只是对象的别名,所以正如 Worrier 解释的那样,引用“总是被取消引用”。

于 2009-01-19T16:16:41.800 回答
2

好的,有一件事困扰着我。如上所述, a 的想法void*是您仍然有一个包含地址的有效变量,但类型被忽略了。这似乎是允许的,因为我们仍然可以使用地址数据——在这种情况下,类型有点多余(或不太重要)。取消引用它是不好的,因为尝试访问成员没有意义,例如p.mem. 我们不知道要引用哪个类,因此不知道要跳转到的内存,要跟随的 vtable 指针。

然而,它本身似乎是有道理的,p因为它只引用对象,但没有引用它的数据。这样做不需要类信息,只需要地址。我知道这绝对没有用,但它在定义事情何时发生故障时很重要。允许这个概念,C++ 引用(不断取消引用但不访问任何东西)例如void& ref = static_cast< &void >(obj)也是有意义的,因此将允许无效引用。我并不是说任何人都应该与负责人讨论,但从“有意义”的角度来看,这似乎是正确的,不是吗?

正如 Luc Touraille 上面指出的(至少,这是我的解释),它可以实现,但问题是语义问题。我可以得出的合理解释是,由于对象变量是内存序列的“标签”,因此类型具有重要的语义价值。因此,指针被认为是具有地址值的变量,将类型视为有些多余 - 不是定义它的关键。

有人会同意吗?

于 2009-04-20T01:08:59.583 回答
0

您可以将引用视为取消引用的指针。从语法上讲,您将引用视为不是指针:您不需要 * 运算符来取消引用它,您可以使用 . 而不是 -> 来访问其成员。

但是,您不能取消引用void指针。正如 Binary Worrier 指出的那样,试图这样做会给你一个编译器错误。如果你不能有一个取消引用的 void 指针,那意味着你不能有一个 void 引用。

于 2009-01-19T17:06:42.697 回答
0

如果它们是,它们在语义上与指针没有区别,并且相当于语法糖。一个参考说,“我指的是这种类型的东西。” 允许 void 或 null 引用会削弱与指针的差异。

当然,引用仍然有可能引用一个不再存在的对象,但这是一个例外。

于 2009-01-19T18:39:07.380 回答
0

以下不是对无效引用概念的辩护。我提供它作为来自野外的轶事。问问自己,这闻起来是不是很有趣。

我的公司是最早在商业上使用 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绑定的类型化引用!片刻的思考可能应该为这个特殊的习语提出更好的替代方案......

于 2009-04-20T02:51:37.277 回答
-3

void是根据定义不存在的东西,因此拥有它的地址是不合逻辑的。

于 2009-01-19T15:16:39.050 回答