1

You cannot declare a void variable:

void fn() {
    void a; // ill-formed
}

Yet this compiles:

void fn() {
    void(); // a void object?
}

What does void() mean? How is it useful? Why is void a; ill-formed, while void() OK?

void fn() {
    void a = void(); // ill-formed
}
geza
  • 28,403
  • 6
  • 61
  • 135
  • 1
    Basically it means nothing or no type – sara Dec 04 '17 at 17:21
  • I imagine this would be useful for templates – Kevin Dec 04 '17 at 17:22
  • 2
    @sara You should take a closer look at the context here. – miradulo Dec 04 '17 at 17:23
  • @SMcCrohan: it's not a duplicate of that. It only answers half of the question. If it is a void expression, then what its value? Why "void a;" is ill-formed, while "void()" is not? – geza Dec 04 '17 at 17:25
  • I disagree this question is a duplicate of the suggested question. The *answer* is the same ( specifically the C++ grammar declares that `void()` is an expression), but it is a very different usage. – Martin Bonner supports Monica Dec 04 '17 at 17:27
  • Let me guess: `void();` is a function declaration like `void f();` where the variable name is optional. – nwp Dec 04 '17 at 17:29
  • The suggested answer discusses `decltype(void())` what is different from the simple use of void. Therefore the questions is not answered by that question. – harper Dec 04 '17 at 17:29
  • @nwp : Your guess is wrong. `int()` is an int value; `void()` is a void value (which is then discarded - you can't actually do anything else with a void value than discard it.) – Martin Bonner supports Monica Dec 04 '17 at 17:35
  • @geza Which part isn't answered by the other question? How it is useful? Or why you can't have objects of void type? I voted to reopen in any case, if you think the questions aren't in the same spirit. – miradulo Dec 04 '17 at 17:36
  • Apparently [you can do](http://coliru.stacked-crooked.com/a/f1c70d475a687449) `void f() { return void(); }` but not `void f() {return void{}; }`. – nwp Dec 04 '17 at 17:41
  • 3
    While `void()` is odd, it's fundamentally consistent with the fact that you can cast expressions to `void`, as in the statement `(void)5;` (which is sometimes done to explicitly signal that a return value is being ignored). In either case the expression produces a void value. You cannot declare a variable of type `void` because it is an incomplete type. – 0x5f3759df Dec 04 '17 at 17:44
  • @miradulo: an example given for it usefulness. But note, that decltype is a C++11 feature. And I think that "void()" is older than that. What was its usefulness before C++11? Why the inconsistency there, that "T a = T();" is OK for all types (for which default constructor is available), except void? If "void()" is OK, why "void a" is not? – geza Dec 04 '17 at 17:49
  • @nwp, interesting, why the difference is there. – geza Dec 04 '17 at 17:50
  • There have been [attempts to make `void` a regular type](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0146r0.html). One can probably find official criticism of that proposal which might lead to insight into the reasoning. – nwp Dec 04 '17 at 17:52
  • @0x5f3759df: isn't it inconsistent? An expression results in a type, which is incomplete. How is it possible? – geza Dec 04 '17 at 18:02
  • 1
    @geza The primary restriction placed on incomplete types is "Objects shall not be defined to have an incomplete type." This does not preclude expression of incomplete type. You can even dereference a pointer to an incomplete type (although it must be a pointer to object type, so void is excluded) and it is legal as long as an lvalue-to-rvalue conversion does not occur on the dereferenced object. – 0x5f3759df Dec 04 '17 at 18:10
  • 1
    Possible duplicate of [Is a statement void(); legal and what is it actually?](https://stackoverflow.com/questions/43096571/is-a-statement-void-legal-and-what-is-it-actually) – André Dec 04 '17 at 18:15
  • @0x5f3759df: ah, that makes sense, thanks! Your comments actually answer the other part of my question. – geza Dec 04 '17 at 18:20

2 Answers2

5

The statement

void();

creates a void value and then discards it. (You can't actually do much with a void value other than discard it or return it.)

The standard† says in 5.2.3 [expr.type.conv

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, whose value is that produced by value-initializing (8.5) an object of type T; no initialization is done for the void() case

Note that it explictaly calls out that void() is legal.

† My link is to N4296 which was the last public committee draft before C++14, however the various versions of the standard do not vary here.


Edit

Is it useful? Explicitly like this? No. I can't see a use for it. It is however, useful in template functions which sometimes do something like:

template <typename T>
T foo() {
    if (prepare_for_for()) {
        return do_foo();
    } else {
        return T();
    }
}

And this will work, even for T == void.

3

Syntactically void() is an explict type conversion written in functional notation (see 5.2.3).

Note that even in "classic" C (C89/90) explicit conversions to void were already allowed. Of course, in C one has to use "classic" C-style cast notation and supply an argument. Any expression can be cast to void in C, including expressions that are already void. This functionality migrated unchanged to cast notation of explicit type conversion in C++ (it is handled by static_cast branch of its functionality, i.e. you can static_cast to void in C++).

Taking the above into account, it is not surprising that conversion to void is also consistently supported by the alternative C++ cast syntax - functional notation. And once you understand that, it is no surprise that it was extended to support the "argument-less" version - void().

The usefulness of this in C++ will include such contexts as generic functions

template <typename T>
T foo() {
  ...;
  return T(); // remains valid even when `T == void`
}

Note that in C++ it is perfectly legal to return void pseudo-values from void functions. The ability to create such void() pseudo-values eliminates the need to write dedicated void specializations for functions like the one above.


As a side note, void() stands for explicit type conversion only if the context forces it to be interpreted as an expression, as in your example. When context calls for a type name (e.g. int foo(void()) or using T = void();) it actually declares a nameless function that returns void and takes no arguments.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • The no-so-easily-acceptable fact is that void is an incomplete type (I've just learned it), yet it can be used as a conversion target. How can one convert a value to an incomplete type? It's a little bit strange. But now I understand the behavior. – geza Dec 04 '17 at 18:37
  • @geza: It is fair to say that `void` is *special* in this regard. In both C and C++ this conversion is given a separate description. – AnT stands with Russia Dec 04 '17 at 18:38