17
votes

Consider the following snippet:

auto f() { return void({}); }
int main() { f(); }

What's exactly the {} in void({})?
How is it interpreted?

Just out of curiosity, of course. Let's go a bit further anyway.

Note that both GCC 6.1 and clang 3.8 compile it with no errors (-std=c++14 -pedantic).
The latter doesn't complain, the former shows a warning:

warning: list-initializer for non-class type must not be parenthesized

Using -pedantic-errors instead, GCC ends with an error while clang compiles it.

Is this discrepancy an error of one of the two compilers?
I mean, is it valid code that should be accepted or not?

1
In some languages, it denotes an empty table or list. However, in type based languages, it should have a type specifier. It does not have anything to do with being an initializer, so imo, it seems like some sort of empty block scope.Chemistpp
Sounds like a clang bug. This goes to [expr.type.conv]/2's "Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized ([dcl.init]) with the initializer.", which make no sense for void.T.C.
it looks as lambda expressions: linkRaindrop7
Lamda expresssions should be [](){} not ({})Chemistpp
@GManNickG No. (void) unused goes via [expr.cast]/4.2 to [expr.static.cast]/6. void(unused) goes to [expr.cast] via the first sentence of [expr.type.conv]/2 ("If the initializer is a parenthesized single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression.") The problem is that ({}) is not a "parenthesized single expression", because {} is not an expression.T.C.

1 Answers

15
votes

Conversions to void type as well as the possibility to returning a void value have been present in C++ language since very beginning. The only part that raises questions is the role of {} in this context.

A quick experiment with clang

int a({});

generates an error message saying

error: cannot initialize a variable of type 'int' with an rvalue of type 'void'

which indicates that clang interprets {} as a void value. This appears to be a non-standard behavior. I don't see any place in language specification that would say that {} should produce a void value in this context.

But since this happens to be the case in clang, there's nothing unusual in void({}) compiling in clang. Any value in C++ can be converted to void type, meaning that as long as the compiler accepts {} in this context, the rest just follows naturally.

In GCC it is actually an error in -pedantic-errors mode

error: list-initializer for non-class type must not be parenthesized

so formally it is an "error", not a "warning" in GCC.


What actually happens here is that the combination of opening ({ and closing }) makes these compilers to interpret it as a GNU C language extension known as Statement Expression (which is incidentally supported by clang as well). This is, for example, what makes the following code compile

int a = ({ 3; });

Under that extension, the expression ({}) is seen as a statement expression of void type. However, this conflicts with the uniform initialization syntax in C++.