1

Why is move constructor being called instead of copy constructor? And when I remove move constructor ,then copy constructor is called .

Used : -fno-elide-constructors to avoid copy elision

#include <iostream>
class test
{
public:
    int x;
    char y;
    bool t;
    test()
    {
        std::cout << " constructed" << std::endl;
    }
    test(test &&temp)
    {
        std::cout << "move constructor" << std::endl;
    }
    test(const test &temp)
    {
        std::cout << "copy constructor" << std::endl;
    }
    template <typename... Args>
    static test create(Args... x)
    {
        test b(x...);
        return b;
    }
};
int main()
{
    test z = test::create();
    test v = test();
}

Output :

 constructed
move constructor
move constructor
 constructed
move constructor

What could be the reason for the above ?

Lion's_Den
  • 121
  • 1
  • 7
  • Why is this surprising to you? Temporary values are automatically moved when assigned – Alan Birtles Apr 02 '21 at 07:38
  • 1
    It's interesting. What compiler are you using? I would have expected copy elision to work so the only output should have been "constructed constructed". – Marius Bancila Apr 02 '21 at 07:42
  • 2
    I tried it: https://godbolt.org/z/8EcbbTKf5 gcc and clang print only `constructed constructed`, even with versions from some years ago. – mch Apr 02 '21 at 07:46
  • What behavior would you expect? Where do you expect copy constructors to be invoked and why? For me, the only thing that is interesting here is that the title of your question is not "why move constructor being used instead of elision?". – zkoza Apr 02 '21 at 07:58
  • @MariusBancila I explicitly used "-fno-elide-constructors" – Lion's_Den Apr 02 '21 at 08:39
  • @zkoza In create function , should it not call copy constructor ? I have not converted lvalue into rvalue using move – Lion's_Den Apr 02 '21 at 08:52
  • @Lion's_Den You don't expect that all passed-by-value function parameters be copy-constructed, do you? Otherwise, move semantics would be useless. Now, why do you expect that returning a value from a function behaves differently? In principle, it doesn't. That's why you can move containers through a function return value. The major difference between function return value and its parameter is who is responsible for the memory management, caller (function return value) or callee (function parameters). All other machinery works essentially in the same way. – zkoza Apr 02 '21 at 17:47

2 Answers2

1

From https://en.cppreference.com/w/cpp/language/return#Automatic_move_from_local_variables_and_parameters, which is mostly the fallback of NRVO:

then overload resolution to select the constructor to use for initialization of the returned value [..]is performed twice:

  • first as if expression were an rvalue expression (thus it may select the move constructor), and if the first overload resolution failed or

[...]

so return b; when copy/move is not elided would do a move-constructor (when available).

For test v = test();, test() is a rvalue, and before C++17, would then use move constructor (if not elided) (when available).

In C++17, that temporary would not even be created, which would avoid the move constructor (mostly as for the copy/move elision version except that the copy/move didn't have to be accessible).

If you remove your move-constructor, as you provide a copy-one, the move-one is not generated, and only copy-constructor is available, and used in place of move-constructor.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

I thought this was basic move semantics. When assigning a temporary variable to a non-temporary variable, so long as a move operator exists (default or used specified), it will be used. Check out the talk from Klaus Igelberger. I think it was cppcon 2019. -apologies. I meant to reply not answer