Title
Remove misleading general allowance for parentheses
Status
open
Section
7.5.4 [expr.prim.paren]
Submitter
Brian Bi

Created on 2025-01-17.00:00:00 last changed 1 month ago

Messages

Date: 2025-01-29.23:00:25

(From submission #661.)

The second sentence in 7.5.4 [expr.prim.paren] paragraph 1 occasionally causes confusion whether parenthesized constructs are allowed where the grammar or other rules do not specifically allow them.

Specific areas of concern are:

  • Parenthesized integer literal with value 0 as a null pointer constant, e.g. (((0ULL))) (supported by all implementations)
  • Parenthesized operand for & when forming a pointer-to-member (7.6.2.2 [expr.unary.op] paragraph 3)
  • Parenthesized non-type template arguments when matching template specializations (13.7.6.1 [temp.spec.partial.general] paragraph 8, 13.10.3.6 [temp.deduct.type] paragraph 8)

Possible resolution:

  1. Change in 6.7.5 [basic.indet] bullet 2.1 as follows:

    • If an indeterminate or erroneous value of unsigned ordinary character type (6.8.2 [basic.fundamental]) or std::byte type (17.2.1 [cstddef.syn]) is produced by the evaluation of:
      • the operand of a parenthesized expression (7.5.4 [expr.prim.paren]),
      • the second or third operand of a conditional expression (7.6.16 [expr.cond]),
      • the right operand of a comma expression (7.6.20 [expr.comma]),
      • ...
      then the result of the operation is an indeterminate value or that erroneous value, respectively.
    • ...
  2. Change in 6.8.2 [basic.fundamental] paragraph 15 as follows:

    A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. An expression of type cv void shall be used only as
    • an expression statement (8.3 [stmt.expr]),
    • the expression in a return statement (8.7.4 [stmt.return]) for a function with the return type cv void,
    • the operand of a parenthesized expression (7.5.4 [expr.prim.paren]),
    • an operand of a comma expression (7.6.20 [expr.comma]),
    • the second or third operand of ?: a conditional expression (7.6.16 [expr.cond]),
    • the operand of a typeid expression (7.6.1.8 [expr.typeid]),
    • ...
  3. Change in 7.3.12 [conv.ptr] paragraph 1 as follows:

    A null pointer constant is an a (possibly parenthesized) integer literal (5.13.2 [lex.icon]) with value zero or a prvalue of type std::nullptr_t. ...
  4. Change 7.5.4 [expr.prim.paren] paragraph 1 as follows:

    A parenthesized expression (E) is a primary expression whose type, result, and value category are identical to those of E. The parenthesized expression is a bit-field if E is a bit-field. If (E) is a discarded-value expression (7.2.3 [expr.context]), so is E. The parenthesized expression can be used in exactly the same contexts as those where E can be used, and with the same meaning, except as otherwise indicated.
  5. Change in 7.5.5.5 [expr.prim.id.dtor] paragraph 2 as follows:

    If the id-expression names a pseudo-destructor, T shall be a scalar type and the id-expression shall appear as the right operand of a class member access (7.6.1.5 [expr.ref]) that forms the (possibly parenthesized) postfix-expression of a function call (7.6.1.3 [expr.call]).
  6. Change in 11.5.1 [class.union.general] paragraph 5 as follows:

    When the left operand of an assignment operator involves a member access expression (7.6.1.5 [expr.ref]) that nominates a union member, it may begin the lifetime of that union member, as described below. For an expression E, define the set S(E) of subexpressions of E as follows:
    • If E is of the form (A), S(E) is S(A).
    • If E is of the form A.B, S(E) contains the elements of S(A), and also contains A.B if B names a union member of a non-class, non-array type, or of a class type with a trivial default constructor that is not deleted, or an array of such types.
    • If E is of the form A[B] and is interpreted as a built-in array subscripting operator, S(E) is S(A) if A is of array type, S(B) if B is of array type, and empty otherwise.
    • Otherwise, S(E) is empty.
  7. Change in 11.9.6 [class.copy.elision] paragraph 1 as follows:

    This elision of object creation, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
    • in a return statement (8.7.4 [stmt.return]) in a function with a class return type, when the expression is the (possibly parenthesized) name of a non-volatile object o with automatic storage duration (other than a function parameter or a variable introduced by the exception-declaration of a handler (14.4 [except.handle])), the copy-initialization of the result object can be omitted by constructing o directly into the function call's result object;
    • in a throw-expression (7.6.18 [expr.throw]), when the operand is the (possibly parenthesized) name of a non-volatile object o with automatic storage duration (other than a function parameter or a variable introduced by the exception-declaration of a handler ) that belongs to a scope that does not contain the innermost enclosing compound-statement associated with a try-block (if there is one), the copy-initialization of the exception object can be omitted by constructing o directly into the exception object;
    • in a coroutine (9.5.4 [dcl.fct.def.coroutine]), ...;
    • when the exception-declaration of a handler (14.4 [except.handle]) declares an object o, ...
  8. Change in 12.2.2.2.1 [over.match.call.general] paragraph 1 as follows:

    In a function call (7.6.1.3 [expr.call])
       postfix-expression ( expression-listopt )
    
    if the postfix-expression names at least one function or function template is an overload set, overload resolution is applied as specified in 12.2.2.2.2 [over.call.func]. If the postfix-expression denotes an object of class type, overload resolution is applied as specified in 12.2.2.2.3 [over.call.object].
  9. Change in 12.2.2.2.2 [over.call.func] paragraph 1 as follows:

    Of interest in 12.2.2.2.2 [over.call.func] are only those function calls in which the postfix-expression ultimately contains an id-expression that denotes one or more functions is an overload set. Such a postfix-expression, perhaps nested arbitrarily deep in parentheses, has one of the following forms: ...
  10. Change in 12.3 [over.over] paragraph 1 as follows:

    An id-expression whose expression E designates the address of an overload set S if E is
    • an id-expression, or
    • of the form & X, where X is a (possibly parenthesized) id-expression, or
    • of the form ( X ), where X is one of these expressions,
    where the terminal name of the id-expression refers to an overload set S and that E appears without arguments. Such an expression is resolved to a function, a pointer to function, or a pointer to member function for a specific function that is chosen from a set of functions selected from S determined based on the target type required in the context (if any), as described below. The target can be .... If the target type contains a placeholder type, placeholder type deduction is performed (9.2.9.7.2 [dcl.type.auto.deduct]), and the remainder of this subclause uses the target type so deduced. The id-expression can be preceded by the & operator. [Note 1: Any redundant set of parentheses surrounding the function name is ignored (7.5.4 [expr.prim.paren]). —end note]
  11. Change in 12.3 [over.over] paragraph 6 as follows:

      int f(double);
      int f(int);
      int (*pfd)(double) = &f;         // selects f(double)
      int (*pfi)(int) = (((&f)));      // selects f(int)
      ...
    
  12. Change in 13.7.6.1 [temp.spec.partial.general] paragraph 8 as follows:

    A non-type argument is non-specialized if it is the unparenthesized name of a non-type parameter. All other non-type arguments are specialized.
  13. Change in 13.10.3.6 [temp.deduct.type] paragraph 8 as follows:

    ... [Note 3: If a type matches such a form but contains no Ts, is, or TTs, deduction is not possible. —end note] Similarly, <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i. [ Note: If a non-type template parameter is parenthesized, deduction is not possible. [ Example:
      template<int N> struct X {};
      template<int N> void f(X<(N)>);   // #1
      void g() { f(X<0>()); }           // error: cannot deduce N in #1
    
    -- end example ] -- end note ]
History
Date User Action Args
2025-01-17 00:00:00admincreate