C++ Logo

std-discussion

Advanced search

Re: Potential defect in the behaviour of the rvalue constructors for std::tuple and std::pair.

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Fri, 10 Nov 2023 18:53:26 -0500
On Fri, Nov 10, 2023 at 6:20 PM Ville Voutilainen via Std-Discussion
<std-discussion_at_[hidden]> wrote:
>
> On Sat, 11 Nov 2023 at 01:01, Bryan Wong via Std-Discussion
> <std-discussion_at_[hidden]> wrote:
> >
> > Hi all,
> >
> > I think I've identified a potential defect in the rvalue constructors for `std::tuple` covered under `22.4.4.1 tuple.cnstr p.20-23` on the latest draft and wanted to ask about it before making a formal report (may also need guidance here if possible, as I've never done it before). I'm also not sure if this issue has been raised before.
> >
> > When using the tuple rvalue constructors, unexpected behaviour occurs with non-movable but copyable types, e.g.
> >
> > struct foo {
> > foo() = default;
> > foo(foo const&) = default;
> > foo& operator=(foo const&) = default;
> > foo(foo&&) = delete;
> > foo& operator=(foo&&) = delete;
> > };
>
> That type is an abomination. If you can copy a type, you can also move
> it, and that just then does the same thing as copy if it can't move.
> The standard library doesn't support types that are copyable but for
> which move operations are ill-formed. Such types make
> no sense, and appear only ever in testsuites.

And GSL::not_null, which is something that people widely encourage the use of.

Broadly speaking, I agree with you about such types, but they do have
extremely corner-case uses (`not_null` for example should only ever be
used as a function parameter).

The core problem is that copyable-but-not-moveable types were never
intended to be a thing, so if you use such a type as a subobject, the
standard generates very odd code for them. This isn't just about
`tuple`; making such a type a member of anything generates nonsense
code. That is:

```
struct container
{
  gsl::not_null<T*> nn;
};
```

While `not_null` is not considered move-constructible, `container`
*is*, unless you explicitly =delete the move constructor. That's just
C++'s default behavior in this case.

Link: https://gcc.godbolt.org/z/z3hbEhbY4

Yes, types like `tuple` *could* override the default behavior, but to
be honest, it would be kind of weird for a C++ standard library
aggregate type to behave so differently from a C++ language aggregate
type. `tuple<not_null>` should behave the same as `container`.

Received on 2023-11-10 23:53:38