11

Since P0593 Implicit creation of objects for low-level object manipulation has been accepted, objects may now be created implicitly in C++20.

Specifically the wording introduced by the proposal allows certain operations (such as std::malloc) to automatically create and start the lifetime of objects of certain types, so-called implicit-lifetime types, if introduction of such objects would cause a program with otherwise undefined behavior to have defined behavior. See [intro.object]/10.

The draft now further states that if there are multiple sets of such objects that could be created implicitly to give the program defined behavior, it is unspecified which of these sets is created. (The relevant sentence does not seem to be present in the last proposal revision that I could access, R5, but is in the draft commit.)

Is there actually a program for which this choice of implicitly created object set is observable? In other words, is there a program with defined, but unspecified, behavior through this new rule, such that it is possible to infer from the output which sets of types of implicit objects (out of more than one possible one) were created?

Or was this sentence merely meant to clarify the program execution on the abstract machine (without observable impact)?

walnut
  • 20,566
  • 4
  • 18
  • 54
  • 2
    (OT) if an implicitly created object is an int, can we call it "implicit int" ? – M.M Mar 10 '20 at 22:50
  • It seems unclear whether the choice of element from the unspecified set must be known at the point of the malloc – M.M Mar 10 '20 at 23:02
  • @M.M I assumed that the choice of set was considered to happen abstractly as a single choice for the whole program execution outside the execution flow, but with the creation happening directly at the operation in question (i.e. `std::malloc`), otherwise you get problems with the definition being recursively depending on the future. – walnut Mar 10 '20 at 23:08
  • I made another question on that topic, https://stackoverflow.com/questions/60627249/ . Of course some more corollaries spring to mind but one question at a time .. – M.M Mar 10 '20 at 23:24
  • The proposal claims that it is impossible to so distinguish, which is important since there is no way for the choice to be made “correctly”, only optimizations to avoid that would otherwise be (*very* strictly) valid. – Davis Herring Mar 12 '20 at 00:11
  • You are misinterpreting "unspecified". It is not about observable behaviour per se. It might be used re observable behaviour but is not limited to that. Here it is used re the state of the abstract machine, and moreover the state options here are explicitly not allowed to affect observable behaviour. – philipxy Mar 27 '20 at 22:04
  • @philipxy My question is precisely whether this use of "*unspecified*" can influence "*observable behavior*" as per [\[intro.abstract\]/5](https://eel.is/c++draft/intro.abstract#5). I am aware that it doesn't need to by the general meaning of the term ([\[intro.abstract\]/3](https://eel.is/c++draft/intro.abstract#3)), but as far as I am aware every other use of the term "*unspecified*" in the standard does in principle allow for programs with multiple possible observable behaviors. – walnut Mar 27 '20 at 22:25
  • My comment is re the last post sentence & also applies to your comment. "Unspecified" is defined, doesn't mention "observable" & can clearly be used wherever appropriate & clearly gives no reason to think that the mere use of it addresses observable behaviour. The rest of your question doesn't rely on thinking it might but I'm just trying to disabuse you of that. I don't know what you could mean by "general meaning" other than "meaning"--as defined. PS The proposal's phrasings in terms of "if otherwise undefined" is poor, it should just *define* in terms of the implicit objects. – philipxy Mar 28 '20 at 08:21

1 Answers1

9

Let's take the example in the standard and change it a little bit:

#include <cstdlib>
struct X { int a, b; };
X *make_x() {
  // The call to std::malloc implicitly creates an object of type X
  // and its subobjects a and b, and returns a pointer to that X object
  // (or an object that is pointer-interconvertible ([basic.compound]) with it),
  // in order to give the subsequent class member access operations
  // defined behavior.
  X *p = (X*)std::malloc(sizeof(struct X) * 2); // me: added the *2
  p->a = 1;
  p->b = 2;
  return p;
}

Previously, there was only one set of valid objects that could be created implicitly in that storage - it had to be exactly one X. But now, we have storage for two Xs, but only write to one of them, and nothing in this program ever touches the rest of the bytes. So there are many different sets of objects that could be implicitly created - maybe two Xs, maybe an X and two ints, maybe an X and eight chars, ...

It's not observable which set is created, because if there were any actual observations, that would reduce the possibilities to only those sets which were valid. If we did something like p[1]->a = 3 then the universe of possibilities collapses down to just the one with two Xs.

In other words, multiple sets of implicitly-created-objects are possibly only when there aren't enough observations in the program to distinguish their validity. If there were a way to distinguish, then by definition, they wouldn't all be valid.

Barry
  • 247,587
  • 26
  • 487
  • 819
  • This is just my guess anyway. – Barry Mar 10 '20 at 22:54
  • So I take it that there simply is no way to distinguish/observe the existence or non-existence of objects of different implicit-lifetime types without undefined behavior. In that case it seems to me that it is the only use of "*unspecified behavior*" in the standard that can't actually lead to different observable outcomes. – walnut Mar 10 '20 at 22:56
  • 1
    Or what if the only accesses are via glvalues of type [cv] `char`, `unsigned char`, or `std::byte`? An object of any trivially-copyable type could also exist there, I guess? – aschepler Mar 10 '20 at 23:00
  • 2
    Even in the original example, it's also possible to create an array object along with the one `X` object. – T.C. Mar 11 '20 at 15:26