8

Moveability allows a great class of optimizations. Yet, it feels that it does this at the cost of punching a hole in the static safety of programs:

After a move, the source object is left in a valid but unspecified state, where some operations are legal, but some are not. (notably see this SO question for discussions around this topic). It would seem that this list of operations, even though it is dependent on each type, can be known at compile time. Yet, the compiler does not warn about incorrect uses of moved-from objects (as this other SO question discusses).

It felt like C++ philosophy to rely on the compiler to validate as much as possible (of what is statically known), one of many example being const-correctness enforcement. Yet, it seems that moved-from object can be used in dangerous ways, without the compiler making attempts (or having any mean) to catch them.

Is there actually a mechanism allowing better diagnostic from the compiler ? If not, why is not there a new qualifier to apply to methods that can be used on a moved from object, or another mechanism allowing equivalent static verifications ?

Community
  • 1
  • 1
Ad N
  • 7,930
  • 6
  • 36
  • 80
  • Such static analysis is expensive, and would prohibitively increase compilation times. You could have a separate [linter](https://en.wikipedia.org/wiki/Lint_%28software%29) check for these things instead. – Some programmer dude Oct 16 '15 at 09:13
  • @JoachimPileborg: I don't see how an automated tool could achieve this. – Lightness Races in Orbit Oct 16 '15 at 09:16
  • 1
    @LightnessRacesinOrbit sure, it could look for attributes that declare methods as dangerous on objects in a specific state. Still I think, it is not only not worth the effort, but also misleading, because design flaws should be solved by changing the design, not by somehow working around them. – cdonat Oct 16 '15 at 09:22
  • @cdonat: Those attributes do not presently exist. – Lightness Races in Orbit Oct 16 '15 at 09:30
  • @LightnessRacesinOrbit the attributes syntax is defined and the static checking tool could define its own attribute names without violating the standard. – cdonat Oct 16 '15 at 09:34
  • @cdonat: And then you'd have to rewrite every standard library implementation to make use of them. – Lightness Races in Orbit Oct 16 '15 at 10:05
  • @LightnessRacesinOrbit why do you think, you'd have to rewrite any libraries to use custom attributes in your own code? Only member functions, that the static code checking tool should protect against usage after move, have to be annotated with such an attribute. All other tools, including the compiler, will ignore all attributes, they don't know. – cdonat Oct 16 '15 at 10:12
  • @cdonat: You can move from many standard types. For this tool to be of any real use, you'd need it to work on those too. – Lightness Races in Orbit Oct 16 '15 at 10:32
  • @LightnessRacesinOrbit 1. Still I think, such a tool would not be useful at all. Anyway, let's just assume, there is a valid case. Then it is already a great tool, when it works only on your own classes. 2. Not all movable objects are left behind in a state like that. 3. even when you chose to change your standard library implementation, that does not say, you have to change all of them to make use of such a tool. – cdonat Oct 16 '15 at 10:37
  • 1
    @cdonat: I read the question as asking for a general-purpose approach. You are proposing a very narrow application. – Lightness Races in Orbit Oct 16 '15 at 10:39
  • I thought the standard is explicit about which operations are legal: The assignment operator and the destructor. If you want more you have to do it specifically for each type. – nwp Oct 16 '15 at 14:13

5 Answers5

4

Moveability allows a great class of optimizations. Yet, it feels that it does this at the cost of punching a hole in the static safety of programs.

Yes, it does.

It would seem that this list of operations, even though it is dependent on each type, can be known at compile time.

But not by the compiler, in general.

Yet, the compiler does not warn about incorrect uses of moved-from objects.

It would be nice, though!

Nope, you're going to have to rely on the documentation. Or just do what I do and, having moved from an lvalue, never use that object again unless you have a tightly controlled scope and some obvious "reset" operation on the object immediately thereafter.

Of course, moving from an rvalue doesn't exhibit this problem.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Thanks for the enlightenment ;) It seems true that the compiler can't with the current level of information it as (-> the knowledge of which operations are defined on moved-from being part of the informal interface). But could not such knowledge be made available to the compiler through some standard mechanism (eg. a new qualifier on methods, like an hypothetical `moved-safe`). – Ad N Oct 16 '15 at 09:14
  • @AdN: In theory, could the language be extended to add such a feature? Yes, probably. Would the feature have enough value to warrant its complexity? No, probably not. Is the ISO working group likely to add it to the language before heat death of the universe? I doubt it!! – Lightness Races in Orbit Oct 16 '15 at 09:15
  • @LightnessRacesinOrbit I'd argue that some sort of compile-time trait system (concepts?) could possibly solve this problem. – CinchBlue Oct 16 '15 at 09:41
  • @VermillionAzure that would require all of your functions to be templates. – TartanLlama Oct 16 '15 at 09:43
  • @TartanLlama Unfortunately, yes. But it would solve such a problem. – CinchBlue Oct 16 '15 at 09:44
  • @VermillionAzure It'll solve that issue, but if you make all your functions templates you'll have `std::integral_constant` problems. – TartanLlama Oct 16 '15 at 09:45
  • @TartanLlama But an unsafe-by-move-function-call ain't one. – CinchBlue Oct 16 '15 at 09:53
  • 1
    The concept of an operation mutating the type of a variable could be a valuable one, and the OP's feature would sort of all out of that for free. I could see a non-zero chance of such a feature being added to C++ at least a week before the heat death of the universe. – Yakk - Adam Nevraumont Oct 16 '15 at 13:58
1

I think it's unlikely that some additional qualifier will be added to the standard for an edge case like this.

One possibility would be to mark your function declarations with #pragma supports_moved_from or something, then build a static-analysis tool to detect calls to functions on potentially moved-from objects and check if they were marked.

You could probably use one of the many Clang tool interfaces for this.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
1

Given n4034 and std::experimental::optional, you could imagine an optional that has a move-from-and-empty operation.

Such an object would be in a clear "not valid" state after being moved from.

You would still need some kind of way to express the state change in a way that C++ compilers would be able to statically check it.

In theory, a language extension that allows operations to mutate the type of a variable during its lifetime could be added to C++, as well as type annotations; then a moved-from value could be changed to have that annotation applied, and operations that are invalid for a moved-from value would trigger compiler errors.

.reset() style operations might be valid on both moved-from values and non-moved-from values, and transition the annotations to "normal" in both cases.

I'm no expert, but I believe Rust attempts to do something like this to solve somewhat similar problems; the programmer has to prove to the type system that certain operations are valid.

This is also similar to recent work on leak/invalid pointer static detection.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

Whenever any of your objects, that do exist, are in a state, that some operations are allowed and others aren't, you probably have a design issue. Usually that is a strong sign for a breach of the single responsibility principle. At least all member functions should have a defined behavior in any case - maybe it will throw an exception.

So whenever you ask, which operations you are allowed to call on an object in a specific state, rethink your design.

cdonat
  • 2,748
  • 16
  • 24
  • `pop_back` (an operation) is not allowed on empty vectors (a specific state of `std::vector`). Do you think the committee should rethink their design ? – Ad N Oct 16 '15 at 09:20
  • yes, `pop_back()` it has undefined behavior on an empty vector. I think, that is a design flaw. I am not aware of a good reason not to define, that it will e.g. silently do nothing in that case. – cdonat Oct 16 '15 at 09:26
  • Well, the easiest implementation for pop_back is `size_--; data_[size].~T();` That one does not check, but call the destructor on a non existing object. The comitee might come to different results, but I think, `if(size) {size_--; data_[size].~T();}` will not impact the performance noticably due to branch predictions. – cdonat Oct 16 '15 at 09:28
  • This one might be an endless argument but... where does the C++ standard talks about branch prediction ? (Or about imposing it as an architectural requirement for the target platforms ?) – Ad N Oct 16 '15 at 09:35
  • @AdN I think that's outside of the scope of the standard, but some like GCC allow you to specify a desired branching or something of the sort. – CinchBlue Oct 16 '15 at 09:42
  • @AdN the standard does nowhere talk about stuff like that, but the committee does take things like that into account, when they decide on the standard. – cdonat Oct 16 '15 at 09:43
0

Is there actually a mechanism allowing better diagnostic from the compiler?

There's nothing stopping a compiler from attempting some static analysis of abuses of moved-from objects: it amounts to much the same as re-marking the object as being in an uninitialised state, and many compilers do issue warning for use of variables that they're not confident have been initialised.

That said, the Standard doesn't normally mandate these kind of diagnostics: they're potentially expensive in compile time, they're typically imperfect (e.g. if you pass a non-const reference to a moved-from object to a function you call, and the implementation's not in the translation unit (i.e. not visible to the compiler) - it can't know whether the moved-from object might be "used" without some kind of prior assignment/reset operation to a meaningful value.

Similarly, if you pass a non-const reference to any object to any function for which the definition's not known, you can't know whether that object might be moved from inside the function.

If not, why is not there a new qualifier to apply to methods that can be used on a moved from object, or another mechanism allowing equivalent static verifications ?

The bottom line is that there are a few times it's relatively easy for a compiler to recognise that an object is is a moved-from state, and many times when it can't. A qualifier might be useful, but doubtless it'd be mistaken for a LOT of people as a guarantee that a moved-from object can't be abused, and there'd be a constant stream of related questions and bugs. Not having a best-effort verification - and having the programmer clearly understand that it's their responsibility to analyse an issue - can sometimes be better than giving developers a false sense of security.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252