为啥不可能有一个对 void 的引用?
Posted
技术标签:
【中文标题】为啥不可能有一个对 void 的引用?【英文标题】:Why is it impossible to have a reference-to-void?为什么不可能有一个对 void 的引用? 【发布时间】:2010-10-02 06:07:57 【问题描述】:为什么不能引用 void?我在 C++ 标准中找到的唯一内容是这一行,位于 8.3.2.1
指定类型“对 cv void 的引用”的声明符格式不正确。
为什么会这样?为什么我不能编写一个接受void&
的“通用”函数?
为了清楚起见,我没有想到使用引用到 void 可能比使用模板更好的有用应用程序,但我只是对禁止这种构造的理由感到好奇。
为了澄清一点,我知道“按原样”使用指向 void 的引用与取消引用指向 void 的指针一样毫无意义。但是,我可以将它转换为对-sometype 的引用以便使用它,不是吗?其实我不明白为什么下面的sn-p可以工作...
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
【问题讨论】:
【参考方案1】:如果您确实提到了 void,您会如何处理它?它不会是数字、字符、指针或类似的东西。您假设的通用函数无法对其执行任何操作,除了获取其地址(而不是其大小)。
“void”有两个用途:否认任何类型知识(如在 void * 中),以及不指定任何东西而不是某物(void 函数返回)。在这两种情况下,除了它可能有一个地址之外,都不能说任何关于 void 的东西。
如果你想不出什么有用的方法,而我却想不出,那至少是某物无用的证据,这很可能至少是这里的部分理由。
【讨论】:
嗯,实际上你可以用 void 引用做一些事情,顺便说一句,它只是关于语法的事情:你可以传递对象而不用 & 取消引用它,这很愚蠢,但我不喜欢使用了很多 & 周围,我发现使用 refs 更清晰 我认为 void 引用有一个用例:与 void 指针(可以是nullptr
)不同,void 引用可以保证引用实际上是指某些东西,甚至如果您不能就其类型提供静态保证。
对 void 的引用具有一个有用的属性,即在构造函数中用作通用引用参数。因此,您可以拥有 Foo a(anyReference) 并且它可以在内部将其转换为 void 指针。它反过来允许您传递引用,并且构造函数可以为您将其转换为指针。当然,它与惯用的 C++ 相去甚远,但 C++ 是一种多范式语言,在一个地方被视为危险的东西在另一个地方是驱动力。如前所述,在构造函数中使用 void 引用比在构造函数中使用 void 指针更安全。
如果void&
没用,没有理由禁止它。该标准是固执己见的,并引入了一个不必要的例外。
我不明白这是如何回答这个问题的。 OP的假设功能如何不起作用?本质上,它将提供与void*
相同的功能,但更确定它不为空【参考方案2】:
首先问问你自己,你将如何取消对 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;
这更有意义吗?
【讨论】:
我完全同意“按原样”使用引用就像取消引用指向 void 的指针:毫无意义。但是,我可以使用参考 reinterpret_cast 以便我可以使用它,我错了吗?我将编辑我的问题以反映这些想法。【参考方案3】:引用是对某事物实例的引用。
某事的实例不能是void
类型。
任何事物的实例都必须具有特定的类型(可能还有基本类型)。
【讨论】:
【参考方案4】:这里总结了已经说过的和我想到的不同的事情。
不允许引用 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
!
因此,即使取地址也意味着取消引用,至少在概念上是这样(在幕后,第一个等价可能是真的,但在语义层面上却不是)。因此,我们绝对不能使用数据,所以空引用是一种异常。
【讨论】:
"&dataref
等同于 &*dataptr
"我不确定我明白了;你的意思是&dataref
是一个右值,不像dataref
?
@curiousguy:我的意思是,由于可以将引用(如Binary Worrier noted)视为不断取消引用的指针,因此获取引用的地址与首先获取地址不同:从概念上讲,更像是解引用一个地址,取结果的地址,这意味着解引用,在处理void
指针时是没有意义的。【参考方案5】:
从技术上讲,所有可以保证的是对对象的引用是它的别名。引擎盖下的引用参数传递是通过指针完成的,这是一个实现细节。这可能会令人困惑,因为引用重用了也是地址的 & 运算符,但请记住,该运算符实际上在不同的上下文中具有不同的含义(在变量或参数声明中,它表示引用类型,否则它是地址,除非它是按位与)。因为它在技术上只是对象的别名,所以正如 Worrier 解释的那样,引用“总是被取消引用”。
【讨论】:
【参考方案6】:好的,有一件事困扰着我。如上所述,void*
的想法是您仍然有一个包含地址的有效变量,但类型被忽略了。这似乎是允许的,因为我们仍然可以使用地址数据——在这种情况下,类型有点多余(或不太重要)。取消引用它是不好的,因为尝试 访问成员 没有意义,例如p.mem
。我们不知道要引用哪个类,也不知道要跳转到的内存,要跟随的 vtable 指针。
但是,p
单独使用似乎是有道理的,因为它只引用对象,而不引用它的数据。这样做不需要类信息,只需要地址。我知道这绝对没有用,但它在定义事情何时发生故障时很重要。允许这个概念,一个 C++ 引用(不断取消引用但不访问任何东西)例如void& ref = static_cast< &void >(obj)
也有意义,因此允许无效引用。我并不是说任何人都应该与负责人讨论,但从“有意义”的角度来看,这似乎是正确的,不是吗?
正如 Luc Touraille 上面指出的(至少,这是我的解释),它可以实现,但问题是语义问题。我可以得出的合理解释是,由于对象变量是内存序列的“标签”,因此类型具有重要的语义价值。因此,指针被视为具有地址值的变量,将类型视为有些多余 - 不是定义它的关键。
有人同意吗?
【讨论】:
【参考方案7】:您可以将引用视为取消引用的指针。从语法上讲,您将引用视为不是指针:您不需要 * 运算符来取消引用它,并且可以使用 .而不是 -> 访问其成员。
但是,您不能取消引用 void
指针。正如 Binary Worrier 所指出的那样,试图这样做会给你一个编译器错误。如果你不能有一个取消引用的 void 指针,那意味着你不能有一个 void 引用。
【讨论】:
"你不能取消引用 void 指针。" 所以问题真的是:你为什么不能呢?【参考方案8】:如果是,它们在语义上与指针没有区别,并且相当于语法糖。一个参考说,“我指的是这种类型的东西。”允许 void 或 null 引用会削弱与指针的差异。
当然,引用仍然有可能引用不再存在的对象,但这是一个例外。
【讨论】:
【参考方案9】:以下内容不是对无效引用概念的辩护。我提供它作为来自野外的轶事。问问自己它闻起来是不是很有趣。
我的公司是最早在商业上使用 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 绑定的类型化引用!稍作思考可能会为这个特定的习语提出更好的替代方案......
【讨论】:
【参考方案10】:void 是根据定义不存在的东西,因此拥有它的地址是不合逻辑的。
【讨论】:
那么不为 void* 的存在辩护,是吗? 我认为您将 value void 与 type void 混淆(如 void* )。帖子的标题也没有做出这种区分。以上是关于为啥不可能有一个对 void 的引用?的主要内容,如果未能解决你的问题,请参考以下文章