代码之家  ›  专栏  ›  技术社区  ›  Luc Touraille

为什么不可能引用void?

  •  53
  • Luc Touraille  · 技术社区  · 17 年前

    为什么不可能引用void?我在C++标准中唯一发现的是这一行 8.3.2.1

    一个声明器,指定类型“引用 履历 “虚空”是病态的。

    为什么是这样?为什么我不能编写一个“泛型”函数来接受 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
    }
    
    10 回复  |  直到 17 年前
        1
  •  39
  •   David Thornley    17 年前

    如果你确实提到了void,你会怎么处理它?它不会是数字、字符、指针或类似的东西。假设的泛型函数无法对其执行任何操作,只能获取其地址(而不是大小)。

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

    如果你想不出一种方法可以让一些东西有用,我也不能,这至少证明了一些东西是无用的,这很可能至少是这里的部分理由。

        2
  •  15
  •   Binary Worrier    17 年前

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

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

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

    有人会同意吗?

        3
  •  6
  •   ChrisW    17 年前

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

        4
  •  3
  •   S.S. Anne    6 年前

    以下是对已经说过的不同事情的总结,也是我想到的。

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


    1. 他们将是完全无用的。

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

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

    当C++问世时,模板成为实现泛型的最佳解决方案。然而,自定义内存管理仍然是可能的,C++和C之间的互操作性是一个主要问题,因此保留了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 !

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

        5
  •  2
  •   Joseph Garvin    17 年前

    这是我的五分钱。

    每个C++表达式都有两个独立的属性:类型和值类别。

    假设问题中的代码是有效的

    void foo(void &data)
    {
       //... 
    }
    

    现在,人们可能会问 表达 data ?当然,它应该是 lvalue of void type 现在,怎么样 (void)5 ?这是一个 prvalue of void type ?怎么样 std::move(data) ?应该 prvalue of void 和 xvalue of void 以根本不同的方式对待?成形怎么样 rvalue reference to void ?这份名单还在继续。

    如果指针指向void,则不会出现这些问题,因为如果 p 是一个指向空的指针,那么 *p 根据标准,这不是一个有效的表达式,我们不需要担心的类型和值类别 p 在无效引用的情况下,相应的声明必须是 数据 它本身是无效的,但如果表达式本身是无效,它怎么能以任何合理的方式使用呢?

        6
  •  2
  •   Luc Touraille    14 年前

    从技术上讲,唯一能保证的是对对象的引用是它的别名。通过指针传递底层引用参数是一个实现细节。由于引用重用&运算符也是的地址,但请记住,运算符在不同的上下文中实际上有不同的含义(在变量或参数声明中,它表示引用类型,否则它是的地址,除非它是按位和)。因为从技术上讲,它只是一个对象的别名,引用“总是被取消引用”,正如Woryer解释的那样。

        7
  •  0
  •   Dima    17 年前

    您可以将引用视为去引用指针。从语法上讲,你把引用当作不是指针:你不需要*运算符来解引用它,你可以使用。而不是->访问其成员。

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

        8
  •  0
  •   JohnMcG    17 年前

    如果是,它们在语义上与指针没有区别,相当于句法糖。引用表示,“我引用的是这种类型的东西。”允许void或null引用会削弱与指针的区别。

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

        9
  •  0
  •   Don Wakefield    17 年前

    以下是…… 不 对无效引用概念的辩护。我把它当作野外轶事来讲述。问问自己,它闻起来是否不奇怪。

    我的公司是最早商业化使用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
    }
    

    因此,这里不是一个空引用,而是一个具有潜在类型的类型化引用 无效的 结合!稍作思考,或许可以为这个特定的习语提供更好的替代方案。..

        10
  •  -3
  •   dmajkic    17 年前

    无效的 根据定义,它是不存在的,所以有它的地址是不合乎逻辑的。