[libcxx-commits] [PATCH] D96523: [libc++] Rewrite the tuple constructors to be strictly Standards conforming

Arthur O'Dwyer via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Fri Nov 12 21:36:23 PST 2021


Quuxplusone added inline comments.


================
Comment at: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR31384.pass.cpp:52
+    std::tuple<Explicit> bar(std::move(d)); ((void)bar);
+#if TEST_STD_VER < 17
+    assert(count == 1);
----------------
ychen wrote:
> Quuxplusone wrote:
> > ychen wrote:
> > > EricWF wrote:
> > > > What's going on here? Why are there different answers depending on the dialect?
> > > Yeah, I'm curious too. GCC/libstdc++ seems to have the same behavior. 
> > https://godbolt.org/z/vGPYqEnfo
> > C++14 sets `bar` to `tuple<Explicit>{ Explicit(42) }` by converting `d`'s `int(42)` using `Explicit(int)` and then building a tuple out of that.
> > C++17 sets `bar` to `tuple<Explicit>{}` by invoking `d.operator tuple<Explicit>()`.
> > Really, this test should be verifying `std::get<0>(bar) == [42 or 0]` as well as `count == [1 or 2]`.
> > 
> > I don't know exactly why C++14 hadn't been considering the conversion operator, or why C++17 started considering it, but I would assume that the reason is buried somewhere in the changes due to LWG2312, LWG2419, LWG2549. (Actually I'm very puzzled as to why the conversion operator wouldn't have been the obviously best match in C++14. Did C++17 change something in the core language about //conversion operators//, completely independent of tuple?)
> > 
> > Note that in October 2021, tuple's constructor constraints changed again due to LWG3121, LWG3155. I assume libc++ has not yet implemented those changes; I don't know if they'll affect this test case again at all.
> After some digging, this seems from rG67ef14fe486e169f937737672d68. But still no idea what is changed in the standard text.
Perhaps this is relevant:
https://godbolt.org/z/hKbncT4zf
GCC 7+ and Clang 6+ agree that in C++17 this is legal, but in C++14 it's not.
```
struct Widget { Widget(const Widget&) = delete; };
struct From { operator Widget() const; };
~~~
>From f;
(void)Widget(f);
```
C++14 seems to treat this as a call to some //constructor// of `Widget` (namely, the copy constructor, with an argument of `f.operator Widget()`); C++17 "more rightly" treats it as a request to produce a Widget by whatever means, such as just calling `operator Widget` alone. ...Or actually, instead, maybe C++17 is //still// treating this as `Widget(f.operator Widget())`; it's just that now `Widget(some-prvalue-widget)` is a no-op instead of trying to call the copy constructor. I wonder if there's any way to observe that difference, or if it's just philosophical at this point.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D96523/new/

https://reviews.llvm.org/D96523



More information about the libcxx-commits mailing list