1

I have a simple code snippet shown below (https://godbolt.org/z/cPT3PhYdj):

int main() {
  int x = 1;
  auto(1); // ok in GCC, error in Clang
  auto{1}; // ok in GCC, error in Clang
  static_cast<void>(auto(x)); // ok
  auto{x}; // ok in GCC, error in Clang
  auto(x); // both error in GCC an Clang
}

Where both GCC and Clang emit an error showing:

// by GCC Trunk [-std=c++23]
<source>: In function 'int main()':
<source>:7:3: error: declaration of 'auto x' has no initializer
    7 |   auto(x); // why
      |   ^~~~
Compiler returned: 1

// by Clang Trunk [-std=c++2b]
<source>:3:8: error: expected unqualified-id
  auto(1); // why
       ^
<source>:3:8: error: expected ')'
<source>:3:7: note: to match this '('
  auto(1); // why
      ^
<source>:4:7: error: expected unqualified-id
  auto{1}; // why
      ^
<source>:6:7: error: expected unqualified-id
  auto{x}; // why
      ^
<source>:7:8: error: redefinition of 'x'
  auto(x); // why
       ^
<source>:2:7: note: previous definition is here
  int x = 1;
      ^
<source>:7:8: error: declaration of variable 'x' with deduced type 'auto' requires an initializer
  auto(x); // why
       ^
6 errors generated.
Compiler returned: 1
  • If C++23 is experimental, and will they be able to fix the ambiguity or change the disambiguation since another auto(expr) is introduced, or just leave it be?

  • Are these expressions supposed to be parsed as explicit type decay conversion auto(expr) or auto{expr} in expression statements or parsed as a declaration?

  • If there is no ambiguity, then which priority comes first:

    • auto(identifier) as auto identifier?, or
    • auto(identifier) as cast expression?
Desmond Gold
  • 1,517
  • 1
  • 7
  • 19
  • 3
    fyi if you are relying on `-std=c++23` features then at present this only has partial support. – Richard Critten May 07 '22 at 14:31
  • 1
    Dup of [How is 'A(tmpVector);' the same as 'A tmpVector;'?](https://stackoverflow.com/questions/24155571/how-is-atmpvector-the-same-as-a-tmpvector) – Language Lawyer May 07 '22 at 21:39
  • It's just weird how special these parentheses are when it comes to `noptr-declarator` being equivalent to `(ptr-declarator)` just to support pointer/reference to function/array declaration grammar. – Desmond Gold May 08 '22 at 00:27
  • @DesmondGold `auto(x)` when used as an expression will behave as an expression. On the other hand it(`auto(x);`) can also be a declaration. This is one of the examples showing how C++ is a **context-sensitive language**. See the [demo](https://wandbox.org/permlink/aaigT15RIzS6vDG8) added at the end of my answer. – Jason May 08 '22 at 03:27

3 Answers3

4

From Explicit cast conversion:

auto ( expression )   (8)     (since C++23)

auto { expression }   (9)     (since C++23)

8,9) The auto specifier is replaced with the deduced type of the invented variable x declared with auto x(expression); (which is never interpreted as a function declaration) or auto x{expression}; respectively. The result is always a prvalue of an object type.

So your usage seems to be allowed(in accordance with ) by the above quoted statement.

Here is a working demo of your code. Note in the linked demo, only the usage auto(x) produces an error, all other cases work fine.


Also note that from PR105516:

auto(x); is interpreted as auto x;. Use +auto(x); if you want that to be an expression.


If there is no ambiguity, then which priority comes first:

This is one of the examples showing how C++ is a context sensitive language. A construct cannot always be understood without knowing its wider contexts. Consider the following example:

int main()
{
    int x = 0   ;
    int k       ;
//------vvvvvvv----->here auto(x) is behaves as an expression as it is used as an expression 
    k = auto(x) ;

    auto(p)     ;  //this a declaration and not an explicit case unlike the above
}
Jason
  • 36,170
  • 5
  • 26
  • 60
  • 2
    From [PR105516](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105516): "*Not a bug, "auto(x);" is interpreted as "auto x;". Use +auto(x); if you want that to be an expression.*" – 康桓瑋 May 07 '22 at 14:53
  • @康桓瑋 Yeah, i was wondering how C++23 resolve that ambiguity. As you mentioned, it resolves it by having a `+` before `auto(x)`. I have added that note in my answer with the appropriate citation/reference. Thanks. – Jason May 07 '22 at 14:57
  • 1
    So what is your conclusion? I don't see C++23 resolve this ambiguity. – 康桓瑋 May 07 '22 at 15:05
  • 4
    I'd rather use `(auto(x))`, `+` isn't necessarily overloaded. – HolyBlackCat May 07 '22 at 15:09
  • @康桓瑋 My conclusion is that it would make more sense to interpret `auto(x)` as a declaration instead of explicit cast. Additionally, as mentioned in [here](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105516) gcc resolves this by having a `+` before `auto(x)` for explicit cast case. Moreover i am not sure how C++23(if this is indeed an ambiguity) resolves this. – Jason May 07 '22 at 15:10
  • 1
    @康桓瑋: There is no ambiguity to resolve. C++23 does not change the meaning of `auto(x)`, so nothing is ambiguous. – Nicol Bolas May 07 '22 at 15:10
  • 1
    I think `(auto(x))` and `auto{x}` are enough – Desmond Gold May 08 '22 at 00:37
4

As far as I can tell the paper introducing this feature didn't make any further relevant changes except to allow auto as type specifier in a functional-style explicit cast.

So intuitively auto here should behave the same as any other type specifier would.

For example, replacing auto with int, it is expected that all of these cases work and are functional-style explicit casts, except

int(x); // auto(x);

This one could according to the grammar also be a declaration of a variable named x of type int (or placeholder type) with parentheses around the declarator and without initializer.

As usual, the grammar is disambiguated by preferring the interpretation as declaration (see [stmt.ambig]/1). At least, I don't think that it should be different for auto. A declaration with auto placeholder type requires an initializer, which is not present in this interpretation, but according to [stmt.ambig]/3 the disambiguation is supposed to be done very early and purely syntactic even if it ends up in an ill-formed declaration. I am not completely sure but I guess this should mean that auto(x); should still be disambiguated as a declaration.

I don't know why Clang gives errors for many of the other lines. I suppose the feature is either implemented only partially or these are implementation bugs (it is a very new feature in an unfinished standard revision).

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • You can refer to the [Demo section](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0849r8.html#Demo) in the original paper, https://godbolt.miator.net/z/Ks43an, modified clang that implements P0849 still rejects `auto(x)`, so I think it should be intentional? – 康桓瑋 May 07 '22 at 15:06
  • @康桓瑋 I don't think any of the demos actually demonstrate the situations in the question. – user17732522 May 07 '22 at 15:09
  • @康桓瑋 The modified clang seems to reject the same lines as Clang rejects in the question, including e.g. `auto{1};`. That doesn't seem correct to me. – user17732522 May 07 '22 at 15:14
  • 1
    Marek Polacek (the guy who [implemented GCC's P0849R8](https://github.com/gcc-mirror/gcc/commit/93810fd673654db9ff16170624a6d36449eab241)) thinks `auto(x)` should be rejected in this [PR](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105516), while Jonathan Wakely said in my [PR](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103653) last year that "*Yes this changed in C++23 so auto(x) creates an rvalue of the decayed type of x*", so I don't really know what's going on. – 康桓瑋 May 07 '22 at 15:23
  • @康桓瑋 I am also not completely sure, but I think it would be surprising if `auto(x);` was _not_ an ill-formed declaration, while e.g. `using T = const int; T(x);` would be. In any case I think the _other_ cases in the question should definitively work. – user17732522 May 07 '22 at 15:30
  • I just realized that the [guy who implemented Clang's P0849R8](https://github.com/llvm/llvm-project/commit/136b2931292083c8d69c09de9b952c86417b2c5d) is also the author of P0849R8... – 康桓瑋 May 07 '22 at 16:05
  • @康桓瑋 That would explain why the demo implementation behaves the same. There is a FIXME in the commit indicating that `auto(&a)->n = 0;` should be treated as expression statement, but currently isn't. There doesn't seem to be any other test case in which a statement starts with `auto(` or `auto{`. I think that indicates that Clang's differing behavior from GCC is by mistake or partial implementation. – user17732522 May 07 '22 at 16:17
2

I think auto(x) (where x is literally an identifier) can still have different meanings in different contexts and thus is subject to [dcl.ambig.res] in C++23.

Unfortunately, the ambiguity is unlikely to be fixed. In the following program (well-formed since C++11), auto(x) = Bar{}; is interpreted as a declaration. If auto(x) were "fixed", auto(x) = Bar{}; would become an expression statement, which would change the existing behavior.

#include <cstdio>
struct Bar {
    Bar()
    {
        std::puts("initialization");
    }
};

struct Foo {
    void operator=(Bar)
    {
        std::puts("assignment");
    }
};

int main()
{
    Foo x{};
    {
        auto(x) = Bar{}; // only prints "initialization"
    }
}
F.v.S.
  • 167
  • 1
  • 8