为什么我们不能声明一个void类型的变量?

时间:2014-09-15 17:10:14

标签: c++ language-lawyer void

我正在寻找标准中对该事实的正式解释。 我已经找到了3.9.1 / 9所说的内容,并尝试用该部分作出解释。

第3.9.1 / 9节,N3797

  

void类型有一组空值。 void类型是一个   不完整的类型,无法完成。它被用作回报   为不返回值的函数键入。任何表达都可以   显式转换为类型cv void(5.4)。 void类型的表达式   应仅用作表达式语句(6.2),作为操作数   逗号表达式(5.18),作为?:( 5.16)的第二个或第三个操作数,   作为typeid,noexcept或decltype的操作数,作为表达式   返回类型为void的函数的返回语句(6.6.3)   或者作为显式转换的操作数来输入cv void。

我不明白它是如何暗示void类型有一组空的值?

假设类型T有一组空值。为什么编译器在遇到以下行时会抛出错误:

extern T v; 

我们可以通过以下方式对不完整类型的变量进行decalre:

#include <iostream>
#include <cstring>

using namespace std;

struct Foo;

extern Foo f; //OK!

int main()
{
}

并且工作正常

DEMO

无法在void类型

上完成
#include <iostream>
#include <cstring>

using namespace std;

extern void f; //compile-time error

int main()
{
}

DEMO

7 个答案:

答案 0 :(得分:23)

您不能声明void类型的变量,因为变量必须具有对象类型或是引用,extern void f;不会声明引用,void不是对象类型

第3节[basic]

  

变量是由非静态数据成员或对象以外的引用声明引入的。

第3.9节[basic.types]

  

对象类型是一种(可能是 cv-qualified )类型,它不是函数类型,不是引用类型,而不是void类型

答案 1 :(得分:9)

&#34; void type是一个不完整的类型&#34;

您无法创建任何不完整类型的变量

&#34; ......无法完成&#34;

虽然您的extern不完整结构的示例可以在稍后完成,但编译器知道类型void的任何声明都无法完成。

答案 2 :(得分:3)

[edit] 以下答案提供了有效的观察,但它们相互矛盾。由于这些可能很有价值,我不会删除它们,但请参阅Ben Voight的答案以及那里的评论,以便采用更直接的方法。

7.1.1 / 8明确允许您对extern声明的观察:

  

声明但未定义的类的名称可以在extern声明中使用。这样的声明只能以不需要完整类类型的方式使用。

void不是&#34;声明但未定义的类&#34;,并且在7.1.1中没有其他例外适用。

此外,3.9 / 5相当明确,事实上它是允许的:

  

已声明但未定义的类,某些上下文(7.2)中的枚举类型,或未知大小或元素类型不完整的数组,是未完全定义的对象类型。 [45]未完全定义的对象类型和void类型是不完整类型(3.9.1)。对象不应定义以使其具有不完整的类型。

强调我的。标准的这一部分对定义和声明之间的差异非常具体,因此通过省略它指定允许声明

答案 3 :(得分:2)

void不完整的类型 - 您只能 声明指向它们的指针并在函数签名中使用它们。显然,extern Foo f; 允许的,因为struct Foo 可以在另一个编译单元中定义(如果它错误将被链接器检测到,但void不能被“定义”(编译器当然知道这一点)所以void在这种情况下非常特殊。

答案 4 :(得分:2)

如果变量有一组空值,则不能用于任何事情。

您无法分配,因为没有可分配的值。

您无法访问它,因为您从未分配过它,因此它具有不确定的值。

由于没有可能的值,因此没有变量的大小。

void仅用作变量位置的占位符。它用作返回类型以指示函数不返回值。它在参数列表中的C中使用,表示该函数不带参数(用于解决语言的原型前版本的歧义)。并且它与指针声明一起使用来创建可以转换为任何其他指针类型的通用指针。在变量声明中没有这样的类似用法。

答案 5 :(得分:2)

因为C和C ++假设可以通过比较它们的地址来比较任何对象的身份,所以它们必须确保所有对象都具有固定的非零大小。如果不是因为这个要求,实际上有很多情况下声明零大小的对象会有所帮助[例如在使用模板的代码中,这些模板包含有时有用且有时不用的字段,或者作为强制结构填充到某个对齐的手段,要求它包含需要这种对齐的元素]。但是,实际上,零大小类型与指定每个对象具有唯一地址的规则不一致的事实不一致,该规则不允许存在可以共享地址的零大小对象。

然而,即使允许使用零大小的对象,也可以指向未知对象&#34;不应该与指向零大小对象的&#34;指针相同&#34;。鉴于类型void*用于前者,这意味着后者应该使用其他东西,这反过来意味着void以外的东西应该是哪种东西一个零大小的对象点。

答案 6 :(得分:0)

嗯 - 我真的没有看到这背后的理由。如果这样我们可以声明一个未知类型的变量,那将会很棒。类似'void *'和未知大小的数组。想象一下这样的代码:

#include <iostream>
#include <cstring>

using namespace std;

extern void f;

int main()
{
    cout << (int &)f << endl; //cout 'f' as it was integer
}

struct {
    int a;
    double b;
} f{};

现在你可以用数组做类似的事情:

#include <iostream>
#include <cstring>

using namespace std;

struct Foo;

extern int arr[];

int main()
{
    cout << arr[2] << endl;
}

int arr[4]{};

生命example

相关问题