Skip to content

CWG2228 Ambiguity resolution for cast to function type #1376

@jfbastien

Description

@jfbastien

Migrating from https://isocpp.org/files/papers/P1018R18.html#issues
CWG2228

Section: 9.3.3 [dcl.ambig.res] Status: review Submitter: Richard Smith Date: 2016-02-02
Consider:

  int x = (int()) + 5; 

This is ill-formed, because 9.3.3 [dcl.ambig.res] paragraph 2 specifies:

An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id.
and thus int() is interpreted as a type-id instead of as a function-style cast, so this is an ill-formed cast to a function type.

This seems to be the wrong disambiguation for all cases where there is a choice between a C-style cast and a parenthesized expression: in all those cases, the C-style cast interpretation results in a cast to a function type, which is always ill-formed.

Further, there is implementation divergence in the handling of this example:

  struct T { int operator++(int); T operator[](int); };
  int a = (T()[3])++; // not a cast

Proposed resolution.

C++ has a blanket disambiguation rule that says if a sequence of tokens can be interpreted as either a type-name or an expression, the type-name interpretation is chosen. This is unhelpful in cases like

    (T())+3

where the current rules make "(T())" a cast to the type "function with no parameters returning T" rather than a parenthesized value-initialization of a temporary of type T. The former interpretation is always ill-formed - you can’t cast to a function type - while the latter could be useful.

Richard’s proposed resolution, cited above, is to avoid the ambiguity by changing the grammar so that cases like "(T())" cannot be parsed as a cast. The wording in the proposal applies that change to a number of different contexts where the ambiguity can come into play. There are two contexts where the change is not applied, however: 1) the operand of typeid, and 2) a template-argument. During our discussion yesterday, there was some support for the idea of applying the change to typeid, as well, although that would be a breaking change for any programs that rely on the current disambiguation to get type information for such function types. There was general agreement, however, to exclude template arguments, since they are used for things like std::function.

CWG would, therefore, like some guidance from EWG on two questions. First, should we apply the new syntax to the operand of typeid, even though it’s a breaking change?

More generally, the question was raised whether we should make this change at all. Although resolving the ambiguity in the other direction would arguably be more convenient in many cases, there is a tension between that convenience and the complexity of the language. In particular, we would be creating a situation where the exact same sequence of tokens would mean two different things, depending on the context in which they appear. Is the convenience worth the cost in complexity?

Meeting: (note 2020-04-29, notes 2020-03-23, note from Core summer 2020, notes from Core Prague 2020, notes from Core 2019-01-07, notes from Core Kona 2019, notes from Core Cologne 2019) This is an issue, we’d like to change the standard to resolve it: SF 0 F 6 N 4 A 3 SA 2

Davis emailed EWG reflector. Long discussion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C++26Targeted at C++26CWGCore

    Type

    No type

    Projects

    Status

    Ready for review

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions