9

This:

void foo(int &&r) {
  std::cout << r << std::endl;
}

int main() {
  int i = 2;
  foo(std::move(i));
  i = 3; //no warning. any way to get some warnings here?
  return 0;
}

Is there no way to tell the compiler to give me an error (or warning) if I accidentally use the variable after I have moved it? I think this would be very convenient. A lot of times I find myself moving variables elsewhere like that, but then I manually have to be VERY CAREFUL that I don't use them afterwards. Now this hasn't caused any problems yet, but who knows down the line... better be safe!

Maybe there are some preprocessor trickery (or pretty widely available compiler extensions) that exists to do this stuff?


More realistic example:

struct HugeStorage {
  std::vector<double> m_vec;
  HugeStorage(std::vector<double> vec) : m_vec(std::move(vec)) { }
};

struct SmallStorage {
  std::vector<double> m_vec;
  SmallStorage(std::vector<double> vec) : m_vec(std::move(vec)) { }
};

std::vector<double> vec_from_data_source() {
  return std::vector<double>(); //only example!!
}

int main() {
  std::vector<double> vec = vec_from_data_source();
  if (vec.size() > 10000)
  {
    HugeStorage storage(std::move(vec));
    //do some things, but I gotta be careful I don't do anything to vec
  }
  else
  {
    SmallStorage storage(std::move(vec));
    //do some things, but I gotta be careful I don't do anything to vec
  }
  return 0;
}
ildjarn
  • 62,044
  • 9
  • 127
  • 211
user2015453
  • 4,844
  • 5
  • 25
  • 27

2 Answers2

11

Is there no way to tell the compiler to give me an error (or warning) if I accidentally use the variable after I have moved it?

The answer is "no, there is no way" (to the best of my knowledge at least, no currently available compiler offers such an option, and for a good reason - see below).

Even if that was possible at all, why would you expect a warning, or even worse an error, to be given in this case? First of all, moving from an integer is not any different than copying it.

Secondly, for most types, assigning a moved-from object of that type is a perfectly legal operation; this is always true of fundamental types like int, and it is definitely true of std::vector, although it might not be true of other types.

In general, whether or not assigning a moved-from object is legal depends on the particular post-conditions of the move operation for that type and on the preconditions of the assignment operator (the assignment operator for types of the Standard Library have no preconditions on the left-hand side argument). This is something a compiler cannot check in the general case.

Therefore, if you were to:

  1. Move from an object for which the move assignment or move constructor places the moved-from object in an unspecified state (that's the case for std::vector), and then;
  2. Invoke any function with preconditions on the state of that object (and that's not the case for the assignment to an std::vector);

That would certainly be bad. On the other hand, the compiler does not have a way to perform a semantic analysis of your program and find out whether this is the case:

A x, y;
...
if (complicatedCondition())
{
    y = move(x);
} 

foo(x); // Did I move from x? And if so, is it safe to call foo()?

Moreover, don't forget that the philosophy of C++ is to give you power and (most often) design guidelines, but "lets you shoot your feet" if you are really trying to do that.

There are dangerous, even meaningless things that you can do in C++ (will your compiler give you a warning, or an error, if you try to delete the same pointer twice?), but the language itself won't prevent you from doing them, under the assumption that you really, really know what you are doing.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • For library types, there is no guarantee that the object is *assignable-to*, only that it is *destructible*. That being said, many library implementations will offer that additional guarantee over what the standard requires. – David Rodríguez - dribeas Feb 25 '13 at 20:23
  • 7
    @DavidRodríguez-dribeas: 17.6.5.15/p1 guarantees that all moved-from std::lib types will be in a valid but unspecified state. That means that unless the types has a precondition on the lhs of an assignment statement, it can be assigned to, even after being moved from. Note that this blanket statement applies to types in the std-lib, and not to types in general. And I am not aware of any std::type that has a precondition on being assigned to. – Howard Hinnant Feb 25 '13 at 20:30
  • 1
    @HowardHinnant: I was mistaken there. Just checked the standard and corroborated what you mention here. Won't remove the comment as that would make your comment an answer to *nothing*. – David Rodríguez - dribeas Feb 25 '13 at 20:47
6
//do some things, but I gotta be careful I don't do anything to vec

Clarification: You need to be careful that you don't do anything to vec that requires a precondition. You can do anything with vec that does not require any preconditions. For example you can assign vec a new value. You can call vec.clear(). You can call vec.size(). But do not call vec.pop_back() because that member function has a precondition.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 3
    In case it's not obvious to everyone, I would clarify that you can't do anything that requires a precondition _without checking that precondition first_. It's fine to call `if (!vec.empty()) vec.pop_back();` because after the call to `vec.empty()` you know the precondition is met – Jonathan Wakely Feb 25 '13 at 20:39