0

Having this simple class:

#include <iostream>

class B 
{ 
public:  
    //default constructor
    B(const char* str = "\0") {
        std::cout << "Constructor called\n";
    }

    //copy constructor
    B(const B& b)   {
        std::cout << "Copy constructor called\n";
    }

    //move constructor
    B(B&& b) {
        std::cout << "Move constructor called\n";
    }
}; 

What is the difference in terms of move semantics between these statements:

B o1 = B("abc");
B o2 = B(B("abc")); 

Are these two lines equivalent?

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
Derek81
  • 145
  • 9
  • 3
    This depends on which C++ version you're using – Human-Compiler Sep 01 '20 at 21:12
  • 2
    This doesn’t address the question, but `”\0”` is an array of **two** nul characters. Use `””` for an empty string. – Pete Becker Sep 01 '20 at 21:14
  • 4
    @Derek81 In the both cases there will be the copy/move elision. – Vlad from Moscow Sep 01 '20 at 21:19
  • There is no move semantic in those two statements. – Eljay Sep 01 '20 at 21:20
  • @Eljay "There is no move semantic in those two statements" - You mean just because in both cases there will be the copy/move elision as Vlad wrote? – Derek81 Sep 01 '20 at 21:30
  • 2
    @Derek81 Yes, there will be copy elision (RVO), but not if you forbid it in C++11 and 14 with `B(B&&) = delete;`. In C++17 the elision is mandatory and it doesn't matter if you `delete` the copy ctor/assignment operator and the move ctor/assignment operator in your example - the code will compile and create 2 objects in total. – Ted Lyngmo Sep 01 '20 at 21:37
  • @TedLyngmo But we can say that these 2 statements are equivalent? – Derek81 Sep 01 '20 at 21:44
  • In C++17, I'd say they are. Before that, hmm, elision wasn't mandatory so I'd say no, you can't rely on that only two objects are created before C++17 even if I don't have a compiler that doesn't elide the copy or move to verify it. Perhaps there's something else that guarantees copy/move elision in those older versions - but if there is, I don't know it. – Ted Lyngmo Sep 01 '20 at 21:57
  • @TedLyngmo But how would it like in the old C++98? Is theses 2 instructions equal in the light in the old C++98? – Derek81 Sep 01 '20 at 22:01
  • 1
    @Derek81: Before C++17, most compilers still had RVO, but it wasn't guaranteed. If your compiler didn't do that, you'd probably see a default->copy, then default->move->copy. – Mooing Duck Sep 01 '20 at 22:05
  • 1
    @Derek81 rvalue references and move semantics didn't exist yet in C++98, they were added in C++11. – Remy Lebeau Sep 01 '20 at 22:12
  • @ Yeah, i know and thata why i've asked. So in the perspective of this old c++98? – Derek81 Sep 01 '20 at 22:32
  • In the perspective of c++98, the compiler's going to see `B(B&& b)`, freak out, and dump half a page of error diagnostics. The question becomes meaningless at that point. – user4581301 Sep 01 '20 at 22:47
  • @user4581301 Heh, I meant not the move constructor definition - B(B&& b) BUT theses `B o1 = B("abc"); B o2 = B(B("abc"));` – Derek81 Sep 01 '20 at 23:37
  • I misunderstood. Sorry about that.In C++98 the problem you get is the scarcities of guarantees. Maybe you get elision. Maybe you don't. No clue about the downvote. Maybe someone figures the question is too broad or too ill defined because there's no specific target Standard revision you're asking about. – user4581301 Sep 02 '20 at 04:34

1 Answers1

7

Since C++17 there is a rule that initialization of an object from a prvalue of the same type means that there is no temporary materialization: the arguments to the prvalue become the arguments to the result object. This is true regardless of what constructors the type has.

In code terms this means T x = T(args); and T x(T(args)); both mean exactly the same thing as T x(args); -- but without the possibility of a Most Vexing Parse.

So both of your examples are equivalent to B o3("abc");.

M.M
  • 138,810
  • 21
  • 208
  • 365