Brace-initialization vs. Parenthesis Bug
Asked Answered
C

1

8

I was filing a GCC bug for this, but I'd rather double-check this.

Consider the following programs:

#include <utility>
template<typename T, typename A>
void F(A&& a) { T(std::forward<A>(a)); } // Note: () syntax.
int main() { int i; F<int&>(i); }

and:

#include <utility>
template<typename T, typename A>
void F(A&& a) { T{std::forward<A>(a)}; } // Note: {} syntax.
int main() { int i; F<int&>(i); }

Latest Clang and MSVC compilers accept both programs. GCC 5 and beyond accept the first program but reject the second, claiming invalid cast of an rvalue expression of type 'int' to type 'int&'.

Is this a GCC bug? Or is this indeed a difference between T{} and T() in the above context (and thus a bug in Clang and MSVC)?

Edit:

The issue can be narrowed down to the following simpler excerpts:

int i; (int&){i};

and

int i; (int&)(i);
Cholera answered 3/10, 2016 at 20:45 Comment(8)
Can you even define and initialize an unnamed lvalue reference? The error seems incorrect anyway.Shulman
@krzaq: I don't see why not. Also, I hit this error in a noexcept expression, so I'd like to understand the situation in an expression context.Cholera
This is core issue 1288. GCC is supposed to be implementing its resolution, but perhaps it wasn't completely fixed.Cellini
Although T{x} is underspecified, see core issue 1521.Cellini
@T.C.: Nice finds. Looks like answer material to me.Persistent
@Cellini Want to write an answer so I can upvote you?Grip
@Cellini I agree with the other comments, these findings apparently settle the issue. I'll update the question to reflect them.Cholera
@Alek What would settle the issue would be GCC fixing this... so it would be good if you could check whether they already have a PR for this, and if not, please file one. :)Separation
C
6

There are two separate issues:

  • The standard is unclear what T{x} should do for reference type T. Currently [expr.type.conv]/1 says that it creates a prvalue of type T, which is nonsense for reference types. This is core issue 1521.
  • The sane thing is probably to have T{x} for reference type T do roughly T __tmp{x}; and then yield the equivalent of static_cast<T>(__tmp) (so xvalue for rvalue reference T and lvalue for lvalue reference T). However, C++11 as published screwed up the specification for list-initialization of references, making it always create a temporary. The result was that int i; int &r{i}; failed to compile because it would attempt to bind r to a temporary copy of i, which is obviously nonsense. This is fixed by core issue 1288, whose resolution GCC is supposed to implement, but it looks like from the error message that it's not completely fixed.
Cellini answered 4/10, 2016 at 19:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.