7

The following code example seems to compile with GCC 12.2, in contrast to Clang 15 and MSVC 19.33, due to an error illegal initializer type 'void'. I would expect the code to be rejected by all three compilers, as it doesn't make sense to construct a (temporary) void object.

auto f() { return void{}; }

using t = decltype(f());

In the example, function f is only used with an unevaluated operand. Is it allowed to use f like this or should even its definition be rejected? (I'm aware of the legal expression void() which creates a prvalue instead, but it's besides the point of this post.)

Passages from the standard that seem relevant:

[basic.types]/5

Incompletely-defined object types and cv void are incomplete types ([basic.fundamental]).

[basic.types]/11.43

The size and layout of an instance of an incompletely-defined object type is unknown.

max66
  • 65,235
  • 10
  • 71
  • 111
303
  • 2,417
  • 1
  • 11
  • 25
  • 2
    `void{}` should be valid. https://stackoverflow.com/questions/53263974/why-doesnt-void-exist?noredirect=1&lq=1 – apple apple Nov 19 '22 at 19:02
  • @appleapple Thanks this could be very useful in some of my function templates that take lambdas but sometimes need to return the default value of the return type of that lambda (which could also be void). – Pepijn Kramer Nov 19 '22 at 19:06
  • All 3 compilers fail to compile this - live - https://godbolt.org/z/W3qKT5d58 Please check the compilation flags etc. – Richard Critten Nov 19 '22 at 19:15
  • @appleapple _"...It is an incomplete type that cannot be completed (consequently, objects of type void are disallowed). ..."_ [Void type](https://en.cppreference.com/w/cpp/language/types#Void_type) – Richard Critten Nov 19 '22 at 19:17
  • 1
    @RichardCritten: Your GCC version is pretty old. – Nicol Bolas Nov 19 '22 at 19:21
  • @RichardCritten Thanks for pointing that out, it appears I tested the code with MSVC trunk instead of MSVC 19.33 (which indeed doesn't compile the code either). I've updated the post accordingly, as the compiler discrepancy is still present due to GCC's results. – 303 Nov 19 '22 at 19:21
  • @NicolBolas I know - it's a best guess for SO questions unless the OP posts compiler versions. 11.3 fails and 12+ compiles ok – Richard Critten Nov 19 '22 at 19:22
  • @appleapple Could you please cite the relevant passages of the C++20 standard that will remove any doubt about the expected behavior of the code in question? I can't seem to find the changes in the C++20 standard that you linked to. (I would suggest to post this as an answer to the question.) – 303 Nov 19 '22 at 19:29

1 Answers1

4

from §expr.type.conv

If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression. Otherwise, if the type is cv void and the initializer is () or {} (after pack expansion, if any), the expression is a prvalue of the specified type that performs no initialization. Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer. If the initializer is a parenthesized optional expression-list, the specified type shall not be an array type.

so void{} is equal to void() that result in valid (cv)void prvalue

apple apple
  • 10,292
  • 2
  • 16
  • 36
  • I guess the working draft [`N4861`](https://timsong-cpp.github.io/cppwp/n4861/) is the closest resemblance of the C++20 standard. The referred to passage [`[expr.type.conv]/2`](https://timsong-cpp.github.io/cppwp/n4861/expr.type.conv#2) does seem to contradict passage [`[basic.types]/5`](https://timsong-cpp.github.io/cppwp/n4861/basic.types#5) and footnote [`[basic.types]/11.39`](https://timsong-cpp.github.io/cppwp/n4861/basic.types#footnote-39). Not sure what to make of it all, should the observed behavior of Clang 15 and MSVC 19.33 be reported as a bug? – 303 Nov 19 '22 at 20:14
  • @303 as latest msvc change the behavior, I'd say the behavior in 19.33 is a bug. (and according my reading of standard) – apple apple Nov 19 '22 at 20:42
  • @303 actually I don't see how `void` doesn't have size change `§expr.type.conv` – apple apple Nov 19 '22 at 20:43
  • @303 and as you state out in question, `void()` is already valid , same should also apply to `void{}`. (and [indeed `void()` is accepted by clang](https://godbolt.org/z/6PMo7KWov)) – apple apple Nov 19 '22 at 20:50
  • 1
    @303 expr.type.conv says that the expression results in a (cv-qualified) `void` _prvalue_. It does not say anywhere that an object of type `void` would be created (which is not possible due to the other standard passage you quote). Creating an object would require that the prvalue expression is used in a context requiring temporary materialization conversion, e.g. a discarded-value expression. But even there the current draft clarifies it doesn't happen for (cv-qualified) `void` prvalues (https://eel.is/c++draft/expr#context-2.sentence-6). The C++20 draft is a bit less clear on that though. – user17732522 Nov 20 '22 at 03:14
  • 1
    @303 Latest MSVC already accepts the code according to DR 2351 and clang already has an open bug report to implement it [here](https://github.com/llvm/llvm-project/issues/51552). – user17732522 Nov 20 '22 at 03:27