Copy elision when creating object inside emplace()

后端 未结 4 849
无人共我
无人共我 2021-02-12 19:00

I see a lot of code at work where people use emplace and emplace_back with a temporary object, like this:

struct A {
    A::A(int, int);
};

vector v;
v         


        
相关标签:
4条回答
  • 2021-02-12 19:33

    The easy answer is no; elision doesn't work with perfect forwarding. But this is c++ so the answer is actually yes.

    It requires a touch of boilerplate:

    struct A {
      A(int, int){std::cout << "A(int,int)\n"; }
      A(A&&){std::cout<<"A(A&&)\n";}
    };
    
    template<class F>
    struct maker_t {
      F f;
      template<class T>
      operator T()&&{ return f(); }
    };
    
    template<class F>
    maker_t<std::decay_t<F>> maker( F&& f ) { return {std::forward<F>(f)}; }
    
    vector<A> v;
    v.emplace_back(maker([]{ return A(1,2); }));
    

    live example.

    Output is one call to A(int,int). No move occurs. In c++17 the making doesn't even require that a move constructor exist (but the vector does, as it thinks it may have to move the elements in an already allocated buffer). In c++14 the moves are simply elided.

    0 讨论(0)
  • 2021-02-12 19:41

    is the compiler able to optimize this and skip the create and copy?

    There is not necessarily a copy involved. If a move constructor is available, there will be a move. This cannot be optimized away, as the direct initialization case will just call the init constructor, while in the other case, the move constructor will be called additionally (including its side-effects).

    Therefore, if possible, you should refactor those codes.

    0 讨论(0)
  • 2021-02-12 19:46

    My question is: is the compiler able to optimize this and skip the create and copy? Or should I really try to fix these occurrences?

    It can't avoid a copy, in the general case. Since emplace_back accepts by forwarding references, it must create temporaries from a pure standardese perspective. Those references must bind to objects, after all.

    Copy elision is a set of rules that allows a copy(or move) constructor to be avoided, and a copy elided, even if the constructor and corresponding destructor have side-effects. It applies in only specific circumstances. And passing arguments by reference is not one of those. So for non-trivial types, where the object copies can't be inlined by the as-if rule, the compiler's hands are bound if it aims to be standard conformant.

    0 讨论(0)
  • 2021-02-12 19:55

    I just want to add

    There is a great 5 minutes lightning talk about copy elision and RVO from Jon Kalb https://youtu.be/fSB57PiXpRw

    Also, you might get different results using different compilers gcc, clang or icc

    See compiler explorer, try different compilers and settings and see for yourself

    https://godbolt.org/g/Yjo9qA

    0 讨论(0)
提交回复
热议问题