Add an attribute: [[uninitialized]]

207 views
Skip to first unread message

Jonathan Coe

unread,
Jun 29, 2016, 3:08:12 PM6/29/16
to std-pr...@isocpp.org
Sometimes default construction or move-from leaves an object in a state where it's no use for anything but later assignment. It would be handy to have an attribute to signify this and help static analyzers.

Any thoughts?

Jon

Sean Middleditch

unread,
Jun 29, 2016, 3:47:59 PM6/29/16
to ISO C++ Standard - Future Proposals, jb...@me.com
Would this concept be extensible to operations besides default construction? Say... move assignment for the source object? Are those cases similar enough that a single attribute makes sense?

A related question is whether this is useful for all constructors? An example I have in production code would be a matrix4x4 class that default-initializes to an identity affine transformation, but also has a tagged constructor (that takes an object of type uninitialized_t, generally initialized from the global constant named 'uninitialized') that leaves the data uninitialized, in case you know you're going to be overwriting the whole thing and don't trust the optimized to remove dead stores (this unfortunately also requires some custom type traits to support the concept of non-default trivial constructors for container optimizations). So, could/should we potentially apply an uninitialized attribute to these kinds of constructors?

Jonathan Coe

unread,
Jun 29, 2016, 4:21:21 PM6/29/16
to std-pr...@isocpp.org
If I have a type that does heap allocation (like a matrix) then a default constructed object would be empty (and useless without assignment) and a moved-from object would have its heap allocated resources stolen and would be empty again. An empty matrix is valid but not useful. If I'm reading from it, odds are it's a bug. Perhaps I could use the attribute [[uninitialized]] as follows:

class matrix
  [[uninitialized]] matrix(); 
  matrix(matrix&& m[[uninitialized]]); // m will be put into an empty state
  ...
};

Maybe [[empty]] would be a better name?

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/8b88f9fd-069f-403b-8252-614e5500e198%40isocpp.org.

Nevin Liber

unread,
Jun 29, 2016, 6:03:39 PM6/29/16
to std-pr...@isocpp.org
On 29 June 2016 at 14:08, Jonathan Coe <jb...@me.com> wrote:
Sometimes default construction or move-from leaves an object in a state where it's no use for anything but later assignment.

Well, it isn't uninitialized, because you have to be able to destroy the object.  We should discourage people from weaken their class invariants; optional<T> is a much better solution for these situations.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Ville Voutilainen

unread,
Jun 29, 2016, 6:10:06 PM6/29/16
to ISO C++ Standard - Future Proposals, Jonathan Coe
On 29 June 2016 at 22:47, Sean Middleditch <sean.mid...@gmail.com> wrote:
> Would this concept be extensible to operations besides default construction?
> Say... move assignment for the source object? Are those cases similar enough
> that a single attribute makes sense?
>
> A related question is whether this is useful for all constructors? An
> example I have in production code would be a matrix4x4 class that
> default-initializes to an identity affine transformation, but also has a
> tagged constructor (that takes an object of type uninitialized_t, generally
> initialized from the global constant named 'uninitialized') that leaves the
> data uninitialized, in case you know you're going to be overwriting the
> whole thing and don't trust the optimized to remove dead stores (this
> unfortunately also requires some custom type traits to support the concept
> of non-default trivial constructors for container optimizations). So,
> could/should we potentially apply an uninitialized attribute to these kinds
> of constructors?


Perhaps such features should be done as part of Contracts?

Nevin ":-)" Liber

unread,
Jun 29, 2016, 6:12:08 PM6/29/16
to std-pr...@isocpp.org
> On Jun 29, 2016, at 5:10 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
>
> Perhaps such features should be done as part of Contracts?

That sounds like a better fit.
--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> +1-312-623-5420

Jonathan Coe

unread,
Jun 29, 2016, 6:13:51 PM6/29/16
to std-pr...@isocpp.org
Suppose I call it [[empty]], does that change your thoughts at all?

My weak aim is to help the optimizer and static analyzer. Weak motivating example follows:

A foo() {
  A a; // where we declared [[empty]] A() = default;
  auto x = some_input();
  if ( x == 0 ) a = f0();
  if ( x != 0 ) a = f1();
  return a;
}

A bar() {
  A a; // where we declared [[empty]] A() = default;
  auto x = some_input();
  if ( x == 0 ) a = f0();
  if ( x == 1 ) a = f1();
  return a; // Compiler can warn about the potential return of an [[empty]] object
}

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Jonathan Coe

unread,
Jun 29, 2016, 6:19:31 PM6/29/16
to Jonathan Coe, std-pr...@isocpp.org
On 29 June 2016 at 23:13, Jonathan Coe <jb...@me.com> wrote:
Suppose I call it [[empty]], does that change your thoughts at all?

My weak aim is to help the optimizer and static analyzer. Weak motivating example follows:

A foo() {
  A a; // where we declared [[empty]] A() = default;
  auto x = some_input();
  if ( x == 0 ) a = f0();
  if ( x != 0 ) a = f1();
  return a;
}

A bar() {
  A a; // where we declared [[empty]] A() = default;
  auto x = some_input();
  if ( x == 0 ) a = f0();
  if ( x == 1 ) a = f1();
  return a; // Compiler can warn about the potential return of an [[empty]] object
}

On 29 June 2016 at 23:02, Nevin Liber <ne...@eviloverlord.com> wrote:
On 29 June 2016 at 14:08, Jonathan Coe <jb...@me.com> wrote:
Sometimes default construction or move-from leaves an object in a state where it's no use for anything but later assignment.

Well, it isn't uninitialized, because you have to be able to destroy the object.  We should discourage people from weaken their class invariants; optional<T> is a much better solution for these situations.

std::optional may be a bad fit because that type says, 'you have to check it'.

I'm interested in cases where being in an empty (or 'uninitialized') state is always a bug.

Thiago Macieira

unread,
Jun 29, 2016, 6:28:48 PM6/29/16
to std-pr...@isocpp.org
On quarta-feira, 29 de junho de 2016 23:13:48 PDT Jonathan Coe wrote:
> Suppose I call it [[empty]], does that change your thoughts at all?
>
> My weak aim is to help the optimizer and static analyzer.

Help doing what?

> A bar() {
> A a; // where we declared [[empty]] A() = default;
> auto x = some_input();
> if ( x == 0 ) a = f0();
> if ( x == 1 ) a = f1();
> return a; // Compiler can warn about the potential return of an [[empty]]
> object
> }

Indeed it can. But the compiler already *does* warn in this case. So what's
the point of the attribute here?

I understand the attribute in the parameter of the move constructor, but if
that one is inline doing:

A (A &&other) { swap(*this, other); }

Then the compiler should already warn on use of the moved-from object.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Jonathan Coe

unread,
Jun 29, 2016, 6:46:48 PM6/29/16
to ISO C++ Standard - Future Proposals


On Wednesday, June 29, 2016 at 11:28:48 PM UTC+1, Thiago Macieira wrote:
On quarta-feira, 29 de junho de 2016 23:13:48 PDT Jonathan Coe wrote:
> Suppose I call it [[empty]], does that change your thoughts at all?
>
> My weak aim is to help the optimizer and static analyzer.

Help doing what?

> A bar() {
>   A a; // where we declared [[empty]] A() = default;
>   auto x = some_input();
>   if ( x == 0 ) a = f0();
>   if ( x == 1 ) a = f1();
>   return a; // Compiler can warn about the potential return of an [[empty]]
> object
> }

Indeed it can. But the compiler already *does* warn in this case. So what's

I'm not being very clear, sorry.
A in this case does have a default constructor that initializes all members, it just puts it into an 'empty' state like a shared pointer with no object or an empty matrix.
I don't think that the compiler can possibly know to warn in such case, that's what the attribute could be helpful for.


the point of the attribute here?

I understand the attribute in the parameter of the move constructor, but if
that one is inline doing:

        A (A &&other) { swap(*this, other); }

Then the compiler should already warn on use of the moved-from object.


I've not seen compiler warnings for use-after-move. I'll have to experiment with this tomorrow.

Thiago Macieira

unread,
Jun 29, 2016, 7:47:58 PM6/29/16
to std-pr...@isocpp.org
On quarta-feira, 29 de junho de 2016 15:46:47 PDT Jonathan Coe wrote:
> I'm not being very clear, sorry.
> A in this case does have a default constructor that initializes all
> members, it just puts it into an 'empty' state like a shared pointer with
> no object or an empty matrix.
> I don't think that the compiler can possibly know to warn in such case,
> that's what the attribute could be helpful for.

So you're saying that this constructor is not trivial but leaves the object in
a state that is not useful. Correct?

Why are you doing that in the first place?

> > the point of the attribute here?
> >
> > I understand the attribute in the parameter of the move constructor, but
> > if
> >
> > that one is inline doing:
> > A (A &&other) { swap(*this, other); }
> >
> > Then the compiler should already warn on use of the moved-from object.
>
> I've not seen compiler warnings for use-after-move. I'll have to experiment
> with this tomorrow.

That kind of new warning, possibly with an attribute would be nice.

Nevin Liber

unread,
Jun 29, 2016, 11:03:27 PM6/29/16
to std-pr...@isocpp.org
On 29 June 2016 at 17:46, Jonathan Coe <jonath...@gmail.com> wrote:


On Wednesday, June 29, 2016 at 11:28:48 PM UTC+1, Thiago Macieira wrote:
On quarta-feira, 29 de junho de 2016 23:13:48 PDT Jonathan Coe wrote:
> Suppose I call it [[empty]], does that change your thoughts at all?
>
> My weak aim is to help the optimizer and static analyzer.

Help doing what?

> A bar() {
>   A a; // where we declared [[empty]] A() = default;
>   auto x = some_input();
>   if ( x == 0 ) a = f0();
>   if ( x == 1 ) a = f1();
>   return a; // Compiler can warn about the potential return of an [[empty]]
> object
> }

Indeed it can. But the compiler already *does* warn in this case. So what's

I'm not being very clear, sorry.
A in this case does have a default constructor that initializes all members, it just puts it into an 'empty' state like a shared pointer with no object or an empty matrix.

But on a shared_ptr I can still call .get() on it, copy it, etc.  Basically, I can call any function that doesn't have a precondition.

You are looking for something far more restrictive than this.  It looks like you are doing so for the two-phase construction anti-pattern.

It's actually worse than that, as you can normally copy, move, etc. things on objects in a weakened invariant state, all of which you want to restrict.  Even the following code becomes problematic:

vector<T> v(1);
v.emplace_back();

As the emplace_back may have to move/copy the objects that it already holds.

It's the same reason we didn't want std::variant to be default constructed into the invalid state.

The invalid state in std::variant only exists as a compromise to get some kind of discriminated union into the standard.  We should not encourage that design for other things.

Nevin Liber

unread,
Jun 29, 2016, 11:07:19 PM6/29/16
to std-pr...@isocpp.org
On 29 June 2016 at 17:19, Jonathan Coe <jb...@me.com> wrote:



Well, it isn't uninitialized, because you have to be able to destroy the object.  We should discourage people from weaken their class invariants; optional<T> is a much better solution for these situations.

std::optional may be a bad fit because that type says, 'you have to check it'.

You don't have to check it.  Take dereferencing: as long as your code can establish that the optional is engaged, you  can dereference it, as in:

optional<T> ot;
//...
ot = T(0);
cout << *ot << endl;

No checking required.
 
I'm interested in cases where being in an empty (or 'uninitialized') state is always a bug.

If it is always a bug, don't make that a valid state of your object.

Jens Åkerblom

unread,
Jun 30, 2016, 6:14:56 AM6/30/16
to ISO C++ Standard - Future Proposals, jb...@me.com
This is a problem with objects that have "special" states in general, so one option is to avoid designing objects like that when possible. To have static analyzers be able to determine that an object is in a potentially invalid state at compile time would not be easy for custom types.

For example

int* p = nullptr;
int i;

if (someCondition)
    p
= &i;

*p = 8;

is easy to analyze since int* is not a custom type and the analyzer knows that dereferencing a null pointer is a bug. 

Now consider

vector<int> ints;

if (someCondition)
   ints
.push_back(1);

ints
.pop_back();

for an analyzer to determine that pop_back has a pre-condition that !vector::empty(), and that the default constructor creates an empty vector, we need something more than a [[empty]] or [[uninitialized]] attribute.

One option would be to specify the state required to call a method and the state the instance is in after the method call but that would be very verbose IMO. Maybe something like:

class VoidPointer
{
public:
   
[[post_state="null"]] explicit VoidPointer() = default;
    [[post_state="null"]] explicit VoidPointer(nullptr_t);
   
explicit VoidPointer(void*);

   
[[pre_state!="null"]] void* get() const;

   
[[post_state="null"]] void reset();

   
...
};

{
   
VoidPointer p(nullptr);
    p
.get(); // error
}

{
   
void* vp = nullptr;
   
VoidPointer p(vp);
    p
.get(); // no error? VoidPointer(void*) does not specify post_state.
}

To me, it just seems like a lot of work for not that much gain but it does have the upside of documenting the pre-requirements and post-state in code.

Matthew Woehlke

unread,
Jun 30, 2016, 9:49:50 AM6/30/16
to std-pr...@isocpp.org
On 2016-06-29 19:47, Thiago Macieira wrote:
> So you're saying that this constructor is not trivial but leaves the object in
> a state that is not useful. Correct?
>
> Why are you doing that in the first place?

...performance? You're familiar with Eigen, yes? (Most Eigen types, or
at least the ubiquitous Matrix type in all its incarnations, does this;
if you just default-construct it, the contents are undefined. I've been
bitten by this before, so I can relate to the desire to be able to warn
about it.)

TBH I'm not sure I agree with this behavior, but some people obviously
think it's useful.

--
Matthew

Thiago Macieira

unread,
Jun 30, 2016, 11:04:04 AM6/30/16
to std-pr...@isocpp.org
On quinta-feira, 30 de junho de 2016 09:49:29 PDT Matthew Woehlke wrote:
> On 2016-06-29 19:47, Thiago Macieira wrote:
> > So you're saying that this constructor is not trivial but leaves the
> > object in a state that is not useful. Correct?
> >
> > Why are you doing that in the first place?
>
> ...performance? You're familiar with Eigen, yes? (Most Eigen types, or
> at least the ubiquitous Matrix type in all its incarnations, does this;
> if you just default-construct it, the contents are undefined. I've been
> bitten by this before, so I can relate to the desire to be able to warn
> about it.)

Those constructors are usually trivial.

I'm asking about why have a non-trivial constructor that leaves the object in
an unusable state.

> TBH I'm not sure I agree with this behavior, but some people obviously
> think it's useful.


--

Matthew Woehlke

unread,
Jun 30, 2016, 12:23:44 PM6/30/16
to std-pr...@isocpp.org
On 2016-06-30 11:03, Thiago Macieira wrote:
> On quinta-feira, 30 de junho de 2016 09:49:29 PDT Matthew Woehlke wrote:
>> On 2016-06-29 19:47, Thiago Macieira wrote:
>>> So you're saying that this constructor is not trivial but leaves the
>>> object in a state that is not useful. Correct?
>>>
>>> Why are you doing that in the first place?
>>
>> ...performance? You're familiar with Eigen, yes? (Most Eigen types, or
>> at least the ubiquitous Matrix type in all its incarnations, does this;
>> if you just default-construct it, the contents are undefined. I've been
>> bitten by this before, so I can relate to the desire to be able to warn
>> about it.)
>
> Those constructors are usually trivial.
>
> I'm asking about why have a non-trivial constructor that leaves the object in
> an unusable state.

Define "unusable". I don't think we're talking about cases where the
object is actually "unusable", just where its state is "unspecified".

For example, QImage(int width, int height, Format format):

"This will create a QImage with uninitialized data."

--
Matthew

Thiago Macieira

unread,
Jun 30, 2016, 1:04:32 PM6/30/16
to std-pr...@isocpp.org
On quinta-feira, 30 de junho de 2016 12:23:19 PDT Matthew Woehlke wrote:
> > I'm asking about why have a non-trivial constructor that leaves the object
> > in an unusable state.
>
> Define "unusable". I don't think we're talking about cases where the
> object is actually "unusable", just where its state is "unspecified".
>
> For example, QImage(int width, int height, Format format):
>
> "This will create a QImage with uninitialized data."

Which is a valid state and the compiler should not warn about.

The example from the OP was more like "any use you make of this prior to
assigning a new object will have unspecified results". It would be akin to
having a std::vector created, but the size left uninitialised.

Matthew Woehlke

unread,
Jun 30, 2016, 2:35:05 PM6/30/16
to std-pr...@isocpp.org
On 2016-06-30 13:04, Thiago Macieira wrote:
> On quinta-feira, 30 de junho de 2016 12:23:19 PDT Matthew Woehlke wrote:
>>> I'm asking about why have a non-trivial constructor that leaves the object
>>> in an unusable state.
>>
>> Define "unusable". I don't think we're talking about cases where the
>> object is actually "unusable", just where its state is "unspecified".
>>
>> For example, QImage(int width, int height, Format format):
>>
>> "This will create a QImage with uninitialized data."
>
> Which is a valid state and the compiler should not warn about.
>
> The example from the OP was more like "any use you make of this prior to
> assigning a new object will have unspecified results". It would be akin to
> having a std::vector created, but the size left uninitialised.

Uh... yeah?

int i;
qDebug() << i; // warning; 'i' is uninitialized
QImage image(64, 64, Qimage::Format_ARGB32);
qDebug() << image.pixel(0, 0); // warning; 'image' is uninitialized

The first warning exists. As I read it, the OP would like the second to
also warn. (You'd need to know what functions fully initialize the
object, though; this probably would not be easy to implement.)

And the above *does* have unspecified results, at least in that the
value you get is unspecified.

--
Matthew

Jonathan Coe

unread,
Jun 30, 2016, 5:37:15 PM6/30/16
to std-pr...@isocpp.org
On 30 June 2016 at 19:34, Matthew Woehlke <mwoehlk...@gmail.com> wrote:
On 2016-06-30 13:04, Thiago Macieira wrote:
> On quinta-feira, 30 de junho de 2016 12:23:19 PDT Matthew Woehlke wrote:
>>> I'm asking about why have a non-trivial constructor that leaves the object
>>> in an unusable state.
>>
>> Define "unusable". I don't think we're talking about cases where the
>> object is actually "unusable", just where its state is "unspecified".
>>
>> For example, QImage(int width, int height, Format format):
>>
>>   "This will create a QImage with uninitialized data."
>
> Which is a valid state and the compiler should not warn about.
>
> The example from the OP was more like "any use you make of this prior to
> assigning a new object will have unspecified results". It would be akin to
> having a std::vector created, but the size left uninitialised.


The example I had in mind was just an undesirable state that coding convention assumes (or hopes) the object won't be found in. I've seen the null state of shared pointers (rightly or otherwise) treated in such a manner. 

'Undesirable' covers 'uninitialised' and "any use you make of this prior to
assigning a new object will have unspecified results".
 
Uh... yeah?


  int i;
  qDebug() << i; // warning; 'i' is uninitialized
  QImage image(64, 64, Qimage::Format_ARGB32);
  qDebug() << image.pixel(0, 0); // warning; 'image' is uninitialized

The first warning exists. As I read it, the OP would like the second to
also warn. (You'd need to know what functions fully initialize the
object, though; this probably would not be easy to implement.)


I really only had the default constructor and move-construction/-assignment in mind.
 
And the above *does* have unspecified results, at least in that the
value you get is unspecified.

--
Matthew
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

sean....@gmail.com

unread,
Jun 30, 2016, 6:26:20 PM6/30/16
to ISO C++ Standard - Future Proposals, jb...@me.com
Perhaps the correct name for such an attribute would be [[unspecified]] as in:

[[unspecified]] atomic() = default;

There are many places in the standard where objects are in an "unspecified" state (i.e. after being moved from, trivial constructors, state of object after an exception for the basic exception guarantee). It would be great of compilers could be told to warn about some set of operations on objects in this state (EoP refers to this as the partially-formed state). A value in an unspecified state has no meaning, at the very least it would be nice to get a compiler warning if the object is read from.

I'm in favor of only allowing destruction and assigning to an object in an unspecified state. I know others have argued that member functions, like vector::clear(), which are equivalent to assignment should be allowed but I don't see any compelling argument for this. There should probably be an attribute to specify that an object satisfies the strong exception guarantee (as basic is assumed).  [[unspecified]] should also be the assumed as the default for moved from objects.

Having such an attribute that compilers could enforce might well alleviate some of the damage done by the current "valid but unspecified" language in the standard. We cannot have both efficiency and safety, provably, and so we need to learn how to take a structured approach for dealing with unsafe operations (like move). <http://sean-parent.github.io/2014/05/30/about-move/> (there is also related info in my "Better Code" talk <https://github.com/sean-parent/sean-parent.github.io/wiki/Papers-and-Presentations>.

I have not done the work to know the pros/cons of such an attribute but I think it is worth consideration.

Nicol Bolas

unread,
Jun 30, 2016, 7:31:02 PM6/30/16
to ISO C++ Standard - Future Proposals, jb...@me.com, sean....@gmail.com

This is all stuff that I think would be better handled in a proper contracts system. That would allow us to handle calling `clear` and other functions on a moved-from `vector`, where appropriate. Indeed, it would allow us to state up-front all kinds of things, not just dealing with whether the state of an object is "valid-but-unspecified".

Sean Parent

unread,
Jun 30, 2016, 8:03:39 PM6/30/16
to Nicol Bolas, ISO C++ Standard - Future Proposals, jb...@me.com
Is there an existing contract system that would have these capabilities for static analysis? How would you state "if this member function throws an exception then the object is left in a partially-formed state" with a contract system? Every contract system I'm aware of consists of runtime checks - there is no runtime check one can make to say "is this value unspecified?" - the answer, by definition, is undefined.

Although I could imagine [[ensures: unspecified()]] where unspecified() is some magic built in function - that would seem to be an abuse of a contract system.

The problem is much closer to a concept issue than one of contracts.
Sean

Matthew Woehlke

unread,
Jul 1, 2016, 10:04:36 AM7/1/16
to std-pr...@isocpp.org
On 2016-06-30 17:37, Jonathan Coe wrote:
> On 30 June 2016 at 19:34, Matthew Woehlke wrote:
>> int i;
>> qDebug() << i; // warning; 'i' is uninitialized
>> QImage image(64, 64, Qimage::Format_ARGB32);
>> qDebug() << image.pixel(0, 0); // warning; 'image' is uninitialized
>>
>> The first warning exists. As I read it, the OP would like the second to
>> also warn. (You'd need to know what functions fully initialize the
>> object, though; this probably would not be easy to implement.)
>
> I really only had the default constructor and move-construction/-assignment
> in mind.

So, replace QImage with a fixed-size Eigen matrix. (Although that, as
Thiago noted, may be a trivial ctor.)

I still think this is a hard problem... not to say it wouldn't be great
if we could solve it, just that it's hard. Again considering the Eigen
matrix, the default ctor leaves its value unspecified, but there are
ways besides assignment to rectify that. The trick is being able to tell
when all parts of the object are specified, when you have methods that
work with only part of the object.

(Traditionally, this is solved by tools like valgrind by marking memory
as uninitialized at the byte level during execution.)

--
Matthew

Nicol Bolas

unread,
Jul 1, 2016, 8:10:44 PM7/1/16
to ISO C++ Standard - Future Proposals, jmck...@gmail.com, jb...@me.com, sean....@gmail.com
On Thursday, June 30, 2016 at 8:03:39 PM UTC-4, Sean Parent wrote:
Is there an existing contract system that would have these capabilities for static analysis? How would you state "if this member function throws an exception then the object is left in a partially-formed state" with a contract system?

Presumably with the same tools that allow you to say that the state of an object will be valid-but-unspecified after calling a move constructor with it.

vector<int> v = {4, 3};
vector
<int> j(std::move(v));
v
[1] = 4;

If a contract system can't catch something as simple as that, then really, what good is it? Isn't the point of a contract system to verify preconditions?

Sean Parent

unread,
Jul 1, 2016, 11:29:14 PM7/1/16
to Nicol Bolas, ISO C++ Standard - Future Proposals, jb...@me.com

A contract system is typically runtime checked (at least everyone I've ever looked at). The reason for the valid but unspecified language is that the object invariants are not violated in a traditional sense. There is no state in the object that can be verified to see if it is unspecified, by definition. In your example, indexing into the object may or may not work. A precondition could be specified as:

T& operator[](size_t i) [[ expects: i < size() ]];

And for an unspecified value size() is unspecified so the precondition may or maynot be satisfied. Adding state would decrease efficiency and would also make nearly everything an optional value. The point at which an object may be unspecified in the code is always known at compile time. Although someone could propose a way to add "unspecified" to a contract system - it would make for an odd mix of runtime and compile time assertions.

One way this could be approached is to start with regular types:

The rules for regular types is that the only valid calls from the regular basis operations on a partially formed object (what the standard calls unspecified) are to destruct and to assign to the object. We could generalize from that to say that it is only valid to write to, or destruct an object.

For the vector example, what would actually fail is the call to size() in the precondition. 

Sean

Jonathan Coe

unread,
Jul 4, 2016, 3:01:05 PM7/4/16
to std-pr...@isocpp.org, Nicol Bolas, jb...@me.com
I think there enough interest in this idea for me to sketch it out in a bit more detail. Is this the right list for a draft language evolution paper?

Thanks for all the input so far,

Jon
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Andrzej Krzemieński

unread,
Jul 8, 2016, 7:59:48 AM7/8/16
to ISO C++ Standard - Future Proposals


W dniu czwartek, 30 czerwca 2016 00:03:39 UTC+2 użytkownik Nevin ":-)" Liber napisał:
On 29 June 2016 at 14:08, Jonathan Coe <jb...@me.com> wrote:
Sometimes default construction or move-from leaves an object in a state where it's no use for anything but later assignment.

Well, it isn't uninitialized, because you have to be able to destroy the object.  We should discourage people from weaken their class invariants; optional<T> is a much better solution for these situations.

While, I agree that a world (of C++ programs) would be better off without "weak invariants", I must observe that every RAII-like object representing a resource and offering move semantics already must provide this "zombie" state. It looks like move-semantics encourages having week invariants.

Regards,
&rze;

Patrice Roy

unread,
Jul 8, 2016, 3:36:05 PM7/8/16
to std-pr...@isocpp.org
Not necessarily; the moved-from state can often be equivalent to a default state, which does not lead to weakening the invariants. We can make move faster by being more agressive, but in many (most?) cases the moved-from state does not need to be zombie-like (taking «zombie-like» as «for which invariants are broken/ weakened for on which dtor and assignment can still be applied»). Am I misunderstanding what you meant by «zombie» state or «weak invariants»?

Cheers!

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Sean Parent

unread,
Jul 8, 2016, 4:31:28 PM7/8/16
to ISO C++ Standard - Future Proposals
The problem is the "not necessarily" part - see the long discussion on the state a variant is left in if assignment fails. The problem is in specifying the _requirements_ of a moved from type. IMO move should be required to be noexcept and the requirements of the moved from type should only be that it is partially formed. Most types can (and do) provide stronger guarantees.

--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/aZG6VRPQ8n8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Andrzej Krzemieński

unread,
Jul 8, 2016, 4:53:54 PM7/8/16
to ISO C++ Standard - Future Proposals


W dniu piątek, 8 lipca 2016 21:36:05 UTC+2 użytkownik Patrice Roy napisał:
Not necessarily; the moved-from state can often be equivalent to a default state, which does not lead to weakening the invariants. We can make move faster by being more agressive, but in many (most?) cases the moved-from state does not need to be zombie-like (taking «zombie-like» as «for which invariants are broken/ weakened for on which dtor and assignment can still be applied»). Am I misunderstanding what you meant by «zombie» state or «weak invariants»?

I mean, even default constructor will often weaken an invariant of a resource-managing class. Not an ideal example, but consider an fstream:

    std
::ofstream f {}; // default constructed file-handle whrapper
    f
.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
    f
<< 0; // oops cannot write to not-a-file

`f` is in a "poor" state where it does not really represent a resource. I cannot write to it unless I manually check whether it really owns a resource at the moment.

Regards,
&rzej;

Reply all
Reply to author
Forward
0 new messages