Doc. no.: | P0963R0 |
---|---|
Date: | 2018-02-05 |
Audience: | Evolution Working Group |
Reply-to: | Zhihao Yuan <zy at miator dot net> |
if (auto [ok, value] = readint(...); ok == format_errc::no_error)
is not as clear and simple as
if (auto [ok, value] = readint(...))
when
if (readint(...))
is starting a valid if
statement.
C++17 structured binding declaration is designed as a variant of variable declarations. More specifically, it is a variant to simple-declaration and for-range-declaration. However, the grammar production condition is, conditionally, also a kind of variable declaration,
if (auto rc = readint(...))
so there is little reason not to allow the above to be replaced by
if (auto [ok, value] = readint(...))
when the return type qualifies contextually convertible to bool and decomposable at the same time.
In a word, we consider the status quo as a wording oversight, presumably affected by the fact that there is no BNF production named “condition-declaration” in the standard text. Therefore we suggest applying the proposed change as a DR against C++17.
It is tempting to add extra semantics given the proposed syntax, such as making the decomposition work (accessing e.get()
, for example) conditional when the underlying object for decomposition, e
, evaluates to false
after contextually converted to bool
. It may enable more programming styles such as throwing get()
. The author considers that this idea complicates the mental model for using the two features together, and decided not to include it in this paper.
It is worthwhile to figure out what array decomposition does in condition. condition bans declaring arrays, so this paper does not allow decomposing array e
either. However, condition accepts array references, which always evaluate to true
, and this is also unchanged in this paper. That is,
if (auto& [a, b, c] = "ht")
works with the proposed change.
Technically we can enable decomposing arrays in conditions, but the author is not motivated to evaluate.
The wording is relative to N4713.
Extend the grammar in 9 [stmt.stmt]/1 as follows:
condition:
expression
attribute-specifier-seqopt decl-specifier-seq declarator brace-or-equal-initializer
attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt[
identifier-list]
brace-or-equal-initializer
Modify 9 [stmt.stmt]/2 as follows:
The rules for conditions apply both to selection-statements and to the
for
andwhile
statements (9.5). The declarator shall not specify a function or an array. The decl-specifier-seq shall not define a class or enumeration. If theauto
type-specifier appears in the decl-specifier-seq, the type of the identifier being declared is deduced from the initializer as described in 10.1.7.4. If identifier-list appears in the condition, the declaration is a structured binding declaration (11.5), where the assignment-expression in the brace-or-equal-initializer shall not have array type if no ref-qualifier is present.
Modify paragraph 3 as follows:
A name introduced by a declaration in a condition (
eithermay be introduced by the decl-specifier-seq,orthe declarator, or the identifier-list of the condition) is in scope from its point of declaration until the end of the substatements controlled by the condition. If the name is redeclared in the outermost block of a substatement controlled by the condition, the declaration that redeclares the name is ill-formed. [ Example:if (int x = f()) { int x; // ill-formed, redeclaration of x } else { int x; // ill-formed, redeclaration of x }
if (auto& [a] = "") { char a; // ill-formed, redeclaration of a }–end example ]
Insert a paragraph between paragraph 3 and 4 in 9 [stmt.stmt]:
The variable of a condition that is an initialized declaration is the declared variable. The variable of a condition that is a structured binding declaration (11.5) is the variable
e
with a unique name.
Rewrite the original paragraph 4 as follows:
If a condition is an expression, the value of the condition is the value of the expression, contextually converted to
bool
for statements other thanswitch
; if that conversion is ill-formed, the program is ill-formed. Otherwise, in aswitch
statement, the value of the condition is the value of the variable of the condition if it has integral or enumeration type, or of that variable implicitly converted to integral or enumeration type otherwise. In a statement other than aswitch
statement, the value of the condition is the value of the variable of the condition contextually converted tobool
(Clause 7). If that conversion is ill-formed, the program is ill-formed. The value of the condition will be referred to as simply “the condition” where the usage is unambiguous.
The proposed change has been implemented in Clang 6.0.0 guarded by -Wbinding-in-condition
: https://godbolt.org/g/hgefUz
The implementation is a one-line-change without counting in diagnosis and tests, giving a sense of how natural the proposed change is.
Thank Richard Smith for encouraging the work.