The following code does not work as intended: //----Example----- #include <vector> #include <cassert> struct A { operator const std::vector<int>&() const { return a_; } std::vector<int> a_; }; int main() { A a {}; const auto& b1 {static_cast<const std::vector<int>&>(a)}; const std::vector<int>& b2 {a}; assert(&a.a_ == &b1); assert(&b1 == &b2); // does not work with gcc 8.3.0 any standard // works with gcc 7.1.0 with c++17 only } //----End of example----- I read through the c++17 standard and did not find any information about this behavior being implementation defined. Diego
The code does work when changing std::vector<int> for any primitive types, i.e. int.
(In reply to Diego from comment #0) > I read through the c++17 standard and did not find any information about > this behavior being implementation defined. It's not implementation defined, but I think all implementations are required to do what GCC 8 does (and other compilers agree). The current behaviour started with r259123 (before that there was an ICE, introduced by r258755).
Reduced: #define assert(C) if (!(C)) { __builtin_puts("Assertion failed: " #C); __builtin_abort(); } struct X { X() { } X(const X&) { } }; struct A { operator const X&() const { return a_; } X a_; }; int main() { A a {}; const auto& b1 {static_cast<const X&>(a)}; const X& b2 {a}; assert(&a.a_ == &b1); assert(&b1 == &b2); // does not work with gcc 8.3.0 any standard } Assertion failed: &b1 == &b2 Aborted (core dumped) If "const X& b2 (a)" is used instead of list-init, the second assertion passes.
> It's not implementation defined, but I think all implementations are required to do what GCC 8 does (and other compilers agree). It worked on linaro 7.4.1 gcc with c++17, gcc 7.1.0 with c++17 only. > If "const X& b2 (a)" is used instead of list-init, the second assertion passes. That seems like an issue no working with {}.
(In reply to Diego Franco from comment #4) > It worked on linaro 7.4.1 gcc with c++17, gcc 7.1.0 with c++17 only. Yes, sometimes old versions have incorrect behaviour and they get fixed. I agree this is surprising, but it's not the only weird property of {} initialization.
Also the brace initialization works with primitive types for the code I posted in the first place. That's definitely a code smell.
This works: #include <vector> #include <cassert> struct A { operator const int&() const { return a_; } int a_; }; int main() { A a {}; const auto& b1 {static_cast<const int&>(a)}; const int& b2 {a}; assert(&a.a_ == &b1); assert(&b1 == &b2); // works
So to summarize, these are the main reason why I believe this should be addressed: - init brace works for references of any type: std::vector<int> a {}; std::vector<int>& b {a}; assert(&a == &b); // works int c {}; int& d {c}; assert(&c == &d); // works - init brace works for user defined conversion reference when using static cast: class A { operator const std::vector<int>&() const {return a_;} std::vector<int> a_; }; A a {}; const auto& b {static_cast<const std::vector<int>&>(a)}; assert(&a == &b); // works - init brace does not work for user defined conversion reference WITHOUT static cast: A a {}; const auto& b {a}; assert(&a == &b); // does not work I think the above behavior is quite unexpected, and does not follow any logic of other behaviors in the language. I found this unexpected behavior by running unit tests. Also, semantic wise is correct to use user-defined-conversion without static cast (above example).
Correction/editing of last section of former comment: ... - init brace does not work for user defined conversion reference WITHOUT static cast: A a {}; const std::vector<int>& b {a}; // changed from "auto" to "std::vector<int>" assert(&a == &b); // does not work ... ...