Doc. no.: | P2174R0 |
Date: | 2020-5-15 |
Audience: | EWG Incubator |
Reply-to: | Zhihao Yuan <zy at miator dot net> |
Compound Literals
This paper proposes to standardize an existing practice, namely compound literals, available in GCC and Clang for C++ language modes. It shares the syntax with C99 compound literals but produces prvalue.
Motivation
When teaching new-expressions, the author found that it is useful to let a student correlate creating an object of automatic storage and an object of dynamic storage:
struct Point
{
int x, y;
};
new Point(3, 4)
Point(3, 4)
Point a(3, 4);
But this encounters some trouble when applying to dynamic arrays. In hypothesis, the author wants to see
new double[]{.3, .2, .1}
double[]{.3, .2, .1}
double a[]{.3, .2, .1};
There is no such grammar for the second line, but it is entirely acceptable to spell such an expression as follows.
(double[]){.3, .2, .1}
Because:
- It is intuitive to group tokens with parenthesis.
- It works already in GCC and Clang.
- C has the same syntax.
Some people, including experts, believe that it is a part of C++ because all relevant C++ compilers, except MSVC, support the proposed syntax. We occasionally find open-source contributors reverting uses of compound literals in people’s C++ code.
However, the compilers powered by the EDG frontend seem to produce lvalues for the proposed syntax. In the following code snippet,
auto&& x = (double[]){ 3, 4, 5 };
x
is of type double(&&)[3]
in GCC and Clang, but is of type double(&)[3]
in NVCC and Intel C++ Compiler. Hence, there is a widely spread existing practice, but with a slightly diverging behavior, which can be better-off if standardized.
Discussion
There is a workaround for it…
using arr_t = double[];
auto&& x = arr_t{ 3, 4, 5 };
It’s silly.
Why don’t you define an alias template?
template<class T> using id = T;
auto&& x = id<double[]>{ 3, 4, 5 };
It’s not only silly but also expert only. The author has never seen the code outside [class.temporary]/6.8.
Let’s focus more on the portability aspect of our situation.
Why producing prvalue rather than closely matching C99?
Given the fact that a typedef followed by braced-init-list produces prvalue in C++,
using arr_t = double[];
auto&& x = arr_t{ 3, 4, 5 };
it would be a wat for a parentasised typedef followed by braced-init-list to produce lvalue:
using arr_t = double[];
auto&& x = (arr_t){ 3, 4, 5 };
To quote Richard,
If we allow this syntax, I think the only consistent and rational semantic model is for T{inits}
to mean the same thing as (T){inits}
, and we should be cognizant that we are somewhat diverging from C’s semantics in adopting that model.
The “Initializer lists” paper discussed an additional issue in ch. 5.5.
Wording
The wording is relative to N4861.
Extend the grammar:
cast-expression:
unary-expression
(
type-id )
cast-expression
(
type-id )
braced-init-list
Insert a new paragraph after [expr.cast]/1:
The result of the expression (T)
cast-expression is of type T
. The result is an lvalue if T
is an lvalue reference type or an rvalue reference to function type and an xvalue if T
is an rvalue reference to object type; otherwise the result is a prvalue. [Note: […] — end note]
If an expression is of form (
type-id )
braced-init-list, let init be the braced-init-list and T
be the type-id. The expression is equivalent to T
init ([expr.type.conv]).
References
Compound Literals
This paper proposes to standardize an existing practice, namely compound literals, available in GCC and Clang for C++ language modes. It shares the syntax with C99 compound literals but produces prvalue.
Motivation
When teaching new-expressions, the author found that it is useful to let a student correlate creating an object of automatic storage and an object of dynamic storage:
But this encounters some trouble when applying to dynamic arrays. In hypothesis, the author wants to see
There is no such grammar for the second line, but it is entirely acceptable to spell such an expression as follows.
Because:
Some people, including experts, believe that it is a part of C++ because all relevant C++ compilers, except MSVC, support the proposed syntax. We occasionally find open-source contributors reverting uses of compound literals in people’s C++ code.
However, the compilers powered by the EDG frontend seem to produce lvalues for the proposed syntax. In the following code snippet,
x
is of typedouble(&&)[3]
in GCC and Clang, but is of typedouble(&)[3]
in NVCC and Intel C++ Compiler. Hence, there is a widely spread existing practice, but with a slightly diverging behavior, which can be better-off if standardized.Discussion
There is a workaround for it…
It’s silly.
Why don’t you define an alias template?
It’s not only silly but also expert only. The author has never seen the code outside [class.temporary]/6.8.
Let’s focus more on the portability aspect of our situation.
Why producing prvalue rather than closely matching C99?
Given the fact that a typedef followed by braced-init-list produces prvalue in C++,
it would be a wat for a parentasised typedef followed by braced-init-list to produce lvalue:
To quote Richard,
The “Initializer lists”[1] paper discussed an additional issue in ch. 5.5.
Wording
The wording is relative to N4861.
Extend the grammar:
Insert a new paragraph after [expr.cast]/1:
References
Stroustrup, Bjarne and Gabriel Dos Reis. N2215 Initializer lists (Rev. 3). http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2215.pdf ↩︎