How to write move so that it can potentially be optimized away?

爷,独闯天下 提交于 2019-12-11 08:55:27

问题


Given the following code:

struct obj {
    int i;
    obj() : i(1) {}
    obj(obj &&other) : i(other.i) {}
};

void f() {
    obj o2(obj(obj(obj{})));
}

I expect release builds to only really create one object and never call a move constructor because the result is the same as if my code was executed. Most code is not that simple though, I can think of a few hard to predict side effects that could stop the optimizer from proving the "as if":

  1. changes to global or "outside" things in either the move constructor or destructor.
  2. potential exceptions in the move constructor or destructor (probably bad design anyway)
  3. internal counting or caching mechanisms changing.

Since I don't use any of these often can I expect most of my moves in and out of functions which are later inlined to be optimized away or am I forgetting something?

P.S. I know that just because an optimization is possible does not mean it will be made by any given compiler.


回答1:


This doesn't really have anything to do with the as-if rule. The compiler is allowed to elide moves and copies even if they have some side effect. It is the single optimization that a compiler is allowed to do that might change the result of your program. From §12.8/31:

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.

So the compiler doesn't have to bother inspecting what happens inside your move constructor, it will likely get rid of any moves here anyway. To demonstrate this, consider the following example:

#include <iostream>

struct bad_mover
{
  static int move_count;
  bad_mover() = default;
  bad_mover(bad_mover&& other) { move_count++; }
};

int bad_mover::move_count = 0;

int main(int argc, const char* argv[])
{
  bad_mover b{bad_mover(bad_mover(bad_mover()))};
  std::cout << "Move count: " << bad_mover::move_count << std::endl;
  return 0;
}
  1. Compiled with g++ -std=c++0x:

    Move count: 0
    
  2. Compiled with g++ -std=c++0x -fno-elide-constructors:

    Move count: 3
    

However, I would question any reason you have for providing a move constructor that has additional side effects. The idea in allowing this optimization regardless of side effects is that a copy or move constructor shouldn't do anything other than copy or move. The program with the copy or move should be exactly the same as without.

Nonetheless, your calls to std::move are unnecessary. std::move is used to change an lvalue expression to an rvalue expression, but an expression that creates a temporary object is already an rvalue expression.




回答2:


Using std::move( tmp(...) ) is completely pointless, the temporary tmp is already an rvalue, you don't need to use std::move to cast it to an rvalue.

Read this series of articles: Want Speed? Pass By Value

You'll learn more and understand better than you will by asking a question on Stackoverflow



来源:https://stackoverflow.com/questions/15040784/how-to-write-move-so-that-it-can-potentially-be-optimized-away

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!