[ub] type punning through congruent base class?

Matt Austern austern at google.com
Fri Jan 17 18:54:28 CET 2014


On Thu, Jan 16, 2014 at 11:38 AM, Herb Sutter <hsutter at microsoft.com> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.open-std.org/pipermail/ub/attachments/20140117/3a494add/attachment.html 


More information about the ub mailing list