Why can't we declare a variable of type void?

2019-01-17 09:30发布

I'm looking for a formal explanation of that fact in the Standard. I've found what 3.9.1/9 says and trying to give an explanation used that section.

Section 3.9.1/9, N3797:

The void type has an empty set of values. The void type is an incomplete type that cannot be completed. It is used as the return type for functions that do not return a value. Any expression can be explicitly converted to type cv void (5.4). An expression of type void shall be used only as an expression statement (6.2), as an operand of a comma expression (5.18), as a second or third operand of ?: (5.16), as the operand of typeid, noexcept, or decltype, as the expression in a return statement (6.6.3) for a function with the return type void, or as the operand of an explicit conversion to type cv void.

I don't understand how it implies from the fact that the void type has an empty set of values?

Suppose that type T has an empty set of values. Why does compiler throw an error when it come across the following line:

extern T v; 

We can decalre a variable of incomplete type in the following way:

#include <iostream>
#include <cstring>

using namespace std;

struct Foo;

extern Foo f; //OK!

int main()
{
}

and it works fine

DEMO

It cannot be done on a void type

#include <iostream>
#include <cstring>

using namespace std;

extern void f; //compile-time error

int main()
{
}

DEMO

7条回答
来,给爷笑一个
2楼-- · 2019-01-17 10:08

You cannot declare a variable of type void because variables must have object type or be references, extern void f; doesn't declare a reference, and void is not an object type:

Section 3 [basic] says that

A variable is introduced by the declaration of a reference other than a non-static data member or of an object.

Section 3.9 [basic.types] says that

An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type.

查看更多
唯我独甜
3楼-- · 2019-01-17 10:09

Well - I really don't see the rationale behind this. It's going to be great if this way we can declare a variable with unknown type. Something like 'void *' and arrays of unknown size. Imagine code like this:

#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{};

You can now actually do something similar with arrays:

#include <iostream>
#include <cstring>

using namespace std;

struct Foo;

extern int arr[];

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

int arr[4]{};

Life example.

查看更多
Rolldiameter
4楼-- · 2019-01-17 10:16

[edit] The answer below makes valid observations, but they're contradicting. As these might be valuable, I'll not delete them, but see Ben Voight's answer and the comments there for a more straightforward approach.

Your observations about extern declarations are specifically allowed by 7.1.1/8:

The name of a declared but undefined class can be used in an extern declaration. Such a declaration can only be used in ways that do not require a complete class type.

void is not a "declared but undefined class", and there's no other exception in 7.1.1 which applies.

Additionally, 3.9/5 is fairly explicit that it is in fact allowed:

A class that has been declared but not defined, an enumeration type in certain contexts (7.2), or an array of unknown size or of incomplete element type, is an incompletely-defined object type. [45] Incompletely defined object types and the void types are incomplete types (3.9.1). Objects shall not be defined to have an incomplete type.

Emphasis mine. This part of the standard is quite specific about the differences between definitions and declarations, so by omission it specifies that declarations are allowed.

查看更多
Explosion°爆炸
5楼-- · 2019-01-17 10:24

Because C and C++ assume that any objects may be compared for identity by comparing their addresses, they must ensure that all objects have fixed non-zero size. Were it not for that requirement, there are in fact many cases where it would be somewhat useful to declare zero-sized objects [e.g. in code which uses templates which contain fields that will sometimes be useful and sometimes not, or as a means of forcing a structure to be padded to a certain alignment requiring that it contain an element requiring such alignment]. As it is, however, zero-size types would be inconsistent with fact that the rule specifying that every object has a unique address includes no exception which would allow for the existence of zero-sized objects that could share an address.

Even if zero-size objects were permissible, however, a "pointer to unknown object" should not be the same as a "pointer to a zero-size object". Given that the type void* is used for the former, that would imply that something else should be used for the latter, which would in turn imply that something other than void should be the type of thing to which a zero-sized object points.

查看更多
我只想做你的唯一
6楼-- · 2019-01-17 10:27

void is an incomplete type - you can only declare pointers to them and use them in function signatures. Obviously, extern Foo f; is permitted because struct Foo can be defined in another compilation unit (and if it's not the error will be detected by the linker), but void can't ever be "defined" (and the compiler knows this, of course) so void's quite special in this case.

查看更多
Anthone
7楼-- · 2019-01-17 10:28

If the variable has an empty set of values, it can't be used for anything.

You can't assign to it, because there are no possible values to assign.

You can't access it, because you never assigned to it, so it has an indeterminate value.

Since there are no possible values, there's no size of the variable.

void is just used as a placeholder in variable places. It's used as a return type to indicate that the function doesn't return a value. It's used in C in the argument list to indicate that the function takes no arguments (to resolve an ambiguity from the pre-prototype version of the language). And it's used with pointer declarations to create generic pointers that can be translated to any other pointer type. There's no such analogous use for it in variable declarations.

查看更多
登录 后发表回答