C++ Logo

SG12

Advanced search

Subject: Re: [ub] type punning through congruent base class?
From: Matt Austern (austern_at_[hidden])
Date: 2014-01-17 11:54:28


On Thu, Jan 16, 2014 at 11:38 AM, Herb Sutter <hsutter_at_[hidden]> wrote:

> Richard, it cannot mean that (or if it does, IMO we have an obvious bug)
> for at least two specific reasons I can think of (below), besides the
> general reasons that it would not be sensical and would violate type safety.
>
>
>
> First, objects must have unique addresses. Consider, still assuming B is
> trivially constructible:
>
> void *p = malloc(sizeof(B));
>
> B* pb = (B*)p;
> pb->i = 0;
>
> short* ps = (short*)p;
> *ps = 0;
>
> This cannot possibly be construed as starting the lifetime of a B object
> and a short object, else they would have the same address, which is
> illegal. Am I missing something?
>
>
>
> Second, this function:
>
>
>
> template<typename T> T* test() {
>
> auto p = (T*)malloc(sizeof(T));
>
> new (p) T{};
>
> return p;
>
> }
>
>
>
> clearly returns a pointer to a T object for any default-constructible type
> T – and that has to be true whether or not T is trivially constructible.
>
>
>
> If the current rules are as Richard says, then it would true that test()
> returns a pointer to a T, unless T is trivially constructible in which case
> it returns a pointer to anything of size T. That doesn’t make any sense,
> and if the rules say that now I’d say we have a defect.
>
>
>
>
>
> The more I see about this, the more I think the lifetime wording (a) is
> correct for non-trivial-ctor cases and (b) is incomplete or incorrect for
> trivial-ctor cases.
>
>
>
> In particular, untyped storage (such as returned from malloc) should never
> be considered of any type T *until after the possibly-trivial T::T has
> run*, such as via placement new. Note I include “possibly-trivial” there
> on purpose. If you don’t call a T ctor, you don’t have a T object – that
> *has* to be true. Otherwise don’t we have multiple contradictions in the
> standard?
>
>
>
> “It’s not a T until the T ctor runs” has to be part of the rules
> somewhere. It is there for non-trivially-constructible types.
>
>
>
> Do we have a defect here that we’re missing that rule for
> trivially-constructible types?
>

I agree that something is missing from the rules. I'm not convinced that
"it's not a T until the T ctor runs" is the right fix. Mostly it's just
that I don't understand what the consequences of that rule would be, and
the standard would have to say something about those consequences. So
consider the following snippet:
  struct T { int x; };
  T x{17}; // 1
  void* pv = malloc(sizeof(T)); // 2
  T* p = static_cast<T*>(pv); // 3
  *p = x; // 4

I'm trying to understand what your rule would imply for that snippet. Does
your rule imply that line 4 is undefined behavior (since, according to your
proposed rule, *p isn't an object of type T)? Or does your rule imply that
*p is an object of type T after line 4 (maybe an assignment might count as
the moral equivalent of a constructor)? Or does your rule imply that this
code snippet is well defined, and that *p may be used to do (some of) the
things you can do with an object of type T but that it is not in fact an
object of type T?

I'm afraid I'm just not sure this is a fruitful line of reasoning. All
three of those possible followups to your rule have odd consequences. Maybe
there's a fourth option I'm not seeing, or maybe it means we need a
different fix.

                         --Matt



SG12 list run by herb.sutter at gmail.com