does counting moves cause a heisenberg effect?
c++, c++11, move, optimization
Solution
Does counting moves cause a Heisenberg effect?
No, not really. If you mean that observation of an event changes the event itself, I believe that's a wrong metaphor in this case.
The risk is rather that your observation will miss the event, as if you had never written those side-effects. Although, in general, side-effects shall be taken into consideration by compilers under the "as if" rule (1.9/1), there are a few important situations where the "as if" rule does not apply: that case is when copy/move elision of a temporary object can be performed by the compiler.
The situations when this is allowed are precisely described in Paragraph 12.8/31 of the C++11 Standard:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies): [...]
The paragraph then goes on listing what the concrete circumstances are. This is not relevant to answer the question, so I omitted them. You can go look them up.
The important bit is that your side effects may be skipped. If the implementation of `std::async()` performs some copies or moves, depending on how this is done, the compiler might elide them even if you have some printouts in there, or more generally, even if you have some side-effects.
Thus, you should not rely on side-effects in your copy constructors, move constructors, or destructors as a portable way of counting how many copies or moves are performed within a function: different compilers, different optimization levels and, of course, different implementations of that function might yield different results.
Problem
This question (code below) std::async and object copy got me thinking about copy/move heavy library implementations. This ones makes 5 copies for example. Countermeasures can be tricky, like in the case of async where passing by reference as opposed to value can cause concurrency problems. Move constructors are also not always cheap. My question is could countermeasures like finding a way to pass by reference really be solving a problem that isn't there? I assume that most if not all of the library functions will end up being inlined, they are often only one or two lines of code. I of course still get five "delete obj" in output because the compiler has to follow the as if rule. If I were to take away the side effect of the destructor, by not printing, would the as if rule go further and end up with only one copy/move in release builds? Are there things I should watch out for when writing move constructors as to not confuse the compiler or unintentionally introduce side effects and stop it from optimization? ``` class obj { public: int val; obj(int a) : val(a) { cout << "new obj" << endl; } ~obj() { cout << "delete obj" << endl; } }; void foo(obj a) { this_thread::sleep_for(chrono::milliseconds(500)); cout << a.val << endl; } int main(int argc, int **args) { obj a(5); auto future = async(foo, a); future.wait(); return 0; } ```