4

Move constructor of class accepts rvalue reference which can be reference to temporary object. So, i have temporary object and appropriate move constructor which can accept reference to temporary object, but move constructor does not called. What`s wrong?

    //g++  5.4.0

#include <iostream>

class foo
{
    int data;
public:
    foo(int v) : data(v) {std::cout << "foo(int)\n";}

    foo(foo&& f)
    {
        std::cout << "moved\n";
    }

    void print()
    {
        std::cout << data;
    }
};

void acceptTmp(foo f)
{
    f.print();
}

int main()
{
    foo f1 = foo(100);
    f1.print();
    acceptTmp(foo(200)); //also does not move
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
Kri
  • 111
  • 2
  • 9
  • 4
    See [How does guaranteed copy elision work?](https://stackoverflow.com/q/38043319/3309790) – songyuanyao Sep 06 '18 at 10:41
  • @songyuanyao as i know c++11 does not have guaranteed copy elision but that behaviour occurs in c++11 – Kri Sep 06 '18 at 10:43
  • 2
    Before C++17 it's not guaranteed, but it's allowed. In fact most decent compilers would apply the optimization. – songyuanyao Sep 06 '18 at 10:45
  • @AlexF no, move assignment also does not called – Kri Sep 06 '18 at 10:46
  • BTW GCC provides the `‑fno‑elide‑constructors` option to disable copy-elision, you may want to try with it. – songyuanyao Sep 06 '18 at 10:49
  • see c++11 copy elision: https://timsong-cpp.github.io/cppwp/n3337/class.copy#31.3 – Oliv Sep 06 '18 at 10:50
  • @songyuanyao Thank you very much. With -fno-elide-constructors move constructor is called. – Kri Sep 06 '18 at 11:06
  • 1
    This is __copy initialization__ _"If T is a class type and the cv-unqualified version of the type of other is T or a class derived from T, the non-explicit constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object."_ see: https://en.cppreference.com/w/cpp/language/copy_initialization – Richard Critten Sep 06 '18 at 11:10
  • Not related, but your GCC is *old*. I suggest updating it. – HolyBlackCat Sep 06 '18 at 11:46

2 Answers2

8

What`s wrong?

Nothing is wrong, except for your expectation that the move constructor would be called.

Prior to C++17, the temporary object would indeed be moved into the argument from the point of view of the abstract machine. However, the standard allows the move to be elided by the compiler, by constructing the temporary directly in place of the object where it would be moved into. You cannot rely on the side-effects of the move constructor to occur.

Post C++17, there is no temporary object or move involved. The standard guarantees that the argument is constructed in place.


The lack of a move is a good thing. Moving an object is potentially slower than not moving it.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

Usually the compiler perform the optimizations and the transformations to the code by applying the as-if rule. The copy/move elision is the only exception to the "as-if" rule. Compiler optimizes the copy and move operations even when there are side effects because the copy and move operations comes at a cost.

Of course before C++17, it depends on the compiler and maybe optimization level. But from c++17 onwards, the eliding is guaranteed.

If you want to instruct the compiler to not perform the elision, you can use -fno-elide-constructors compiler flag.

Please see the below links to understand more about the copy/move ellison:

What are copy ellision?

cpp_enthusiast
  • 1,239
  • 10
  • 28
  • There is no (N)RVO in the example code. Return Value Optimization is a specific copy elision when function returns by value. There are no function returning a `foo` by value in the example. – eerorika Sep 06 '18 at 13:27
  • @user2079303 -- Sorry, then I misunderstood the question. I will edit my answer. Thanks for pointing it out. Is my answer ok now? – cpp_enthusiast Sep 06 '18 at 13:41
  • Seems OK to me. – eerorika Sep 06 '18 at 13:49