Why does the compiler choose the copy ctor after calling move - c++

For the following code, I know that because I implement a copy constructor the default move is blocked by the compiler, what I don't understand is why the compiler choose the copy constructor, and not issuing an error?
Can a rvalue reference be the parameter for a reference function if no rvalue ref function exists?
If not, what do happens here?
#include <iostream>
using namespace std;
class A{
public:
A(){printf("constructor\n");}
A(const A& a){printf("copy constructor");}
};
int main()
{
A a;
A b = std::move(a);
return 1;
}
The output is:
constructor
copy constructor
Thanks

The reason is that rvalue expressions can be bound to function parameters that are const lvalue references.
Also note that const rvalue expressions cannot be bound to rvalue references:
class A{
public:
A(){printf("constructor\n");}
A(const A& a){printf("copy constructor");}
A(A&&) = default; // <-- has move ctor
};
int main()
{
const A a; // <-- const!
A b = std::move(a); // <-- copy not move!!!
return 1;
}
The code above still calls the copy constructor, even though there is a move constructor.

std::move will take your named value and turn it into an rvalue expression, nothing more.
And, just like for any other rvalue, if it can't be moved, it'll be copied.
An rvalue of type T can be bound to a const T& just fine.

Related

why non-movable Object still a copy

Consider the following code, Entity object is non-movable. I know that std::move(Obj) just cast the Obj to a rvalue reference Obj. And I also know that rvalue reference variable is still a lvalue object. But I still confusing why statement auto temp = std::move(a) can call copy constructor, statement a = std::move(b); can call copy assignment operator. Since, std::move(a) is a rvalue reference, why it can still call lvalue constructor.
#include <iostream>
template<typename T>
void my_swap(T& a, T& b) {
auto temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
class Entity{
int _id;
public:
Entity(int id) :_id(id) {std::cout << "construtor\n" << std::endl;}
Entity(const Entity& other) {
_id = other._id;
std::cout << "copy constructor\n" << std::endl;
}
Entity& operator= (const Entity& other) {
if (&other == this) return *this;
this->_id = other._id;
std::cout << "copy assignment operator\n";
return *this;
}
};
int main() {
Entity e1 = Entity(10);
Entity e2 = Entity(20);
my_swap(e1,e2);
return 0;
}
Entity object is non-movable
No. Even it doesn't have move constructor/assignment-operator, it has copy constructor/assignment-operator taking lvalue-reference to const. std::move(a) and std::move(b) are rvalue (xvalue) expressions and they could be bound to lvalue-reference to const.
You might also check std::is_move_constructible:
Types without a move constructor, but with a copy constructor that accepts const T& arguments, satisfy std::is_move_constructible.
If you want to make your Entity non-movable, then add the move-constructor and move-assignment operator as deleted:
Entity(Entity&& other) = delete;
Entity& operator= (Entity&& other) = delete;
Considering the following code, Entity object is non-movable.
No, it isn't. Entity is movable.
But I still confusing why statement auto temp = std::move(a)
Lvalue reference to const can be bound to an rvalue. The copy constructor accepts an lvalue reference to const parameter. That parameter can be bound to the std::move(a) rvalue argument.
And I also know that rvalue reference variable is still a lvalue object.
Not quite. References are not objects. An id-expression that names anything, including an rvalue reference varaible, is an lvalue.
Entity is movable. Does is because there exists default move constructor/assignment-operator.
It is movable because it has a copy constructor/assignment operator, and it doesn't have deleted move constructor/assignment operator.

Why I can't move this vector in the copy constructor of my custom class?

class TestClass
{
public:
TestClass(){
cout<<"constructor"<<endl;
p = {1,2,3};
cout<<(unsigned int *)(this->p.data())<<endl;
}
TestClass(const TestClass& test): p(std::move(test.p))
{
cout <<"copy constructor"<<endl;
cout<<(unsigned int *)(this->p.data())<<endl;
}
TestClass(TestClass && test): p(std::move(test.p))
{
cout <<"move constructor"<<endl;
cout<<(unsigned int *)(this->p.data())<<endl;
}
private:
std::vector<int> p;
};
int main()
{
TestClass t{};
TestClass p{t};
TestClass s{std::move(p)};
return 0;
}
And the output is
constructor
0xb92bf0
copy constructor
0xb915b0
move constructor
0xb915b0
I am just wondering why the address below constructor is different from the one below copy constructor. From what I understand, even it is a copy constructor, but I used std::move to get a rvalue reference and vector's move constructor should be called, so they should be same object.
std::move just casts whatever is passed to it to an xvalue, so rvalue-references can bind to it and may steal its resources. Here:
TestClass(const TestClass& test): p(std::move(test.p))
std::move will produce an expression of type const std::vector<int> &&, which, as you can see, has a const qualifier. If you check the copy- and move-constructors of std::vector on [vector], you'll see that the move-constructor expects an expression of type std::vector<T> &&, and the copy-constructor expects a const std::vector<T> &:
vector(const vector& x);
vector(vector&&) noexcept;
Compare the result of std::move(test.p) to these two constructors. Because an rvalue-reference doesn't bind to types with const qualifiers (unless the rvalue-reference is const-qualified), then the move-constructor overload isn't a good candidate. The other candidate (copy-constructor) does accept a const-qualified type, and since xvalues have the same properties as rvalues:
http://en.cppreference.com/w/cpp/language/value_category#rvalue
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
, the copy-constructor is a good candidate and is selected.

Objects that can be initialized but not assigned

I need to create a class whose objects can be initialized but not assigned.
I thought maybe I could do this by not defining the assignment operator, but the compiler uses the constructor to do the assignment.
I need it to be this way:
Object a=1; // OK
a=1; // Error
How can I do it?
Making a const will do the trick
const Object a=1; // OK
Now you won't be able to assign any value to a as a is declared as const. Note that if you declare a as const, it is necessary to initialize a at the time of declaration.
Once you have declared a as const and also initialized it, you won't be able to assign any other value to a
a=1; //error
You can delete the assignment operator:
#include <iostream>
using namespace std;
struct Object
{
Object(int) {}
Object& operator=(int) = delete;
};
int main()
{
Object a=1; // OK
a=1; // Error
}
Alternative Solution
You can use the explicit keyword:
#include <iostream>
using namespace std;
struct Object
{
explicit Object(int) {}
};
int main()
{
Object a(1); // OK - Uses explicit constructor
a=1; // Error
}
Update
As mentioned by user2079303 in the comments:
It might be worth mentioning that the alternative solution does not prevent regular copy/move assignment like a=Object(1)
This can be avoided by using: Object& operator=(const Object&) = delete;
I hoped this would be so by not defining the assignment operator
This doesn't work because the copy assignment operator (which takes const Object& as parameter) is implicitly generated. And when you write a = 1, the generated copy assignment operator will be tried to invoke, and 1 could be implicitly converted to Object via converting constructor Object::Object(int); then a = 1; works fine.
You can declare the assignment operator taking int as deleted (since C++11) explicitly; which will be selected prior to the copy assignment operator in overload resolution.
If the function is overloaded, overload resolution takes place first, and the program is only ill-formed if the deleted function was selected.
e.g.
struct Object {
Object(int) {}
Object& operator=(int) = delete;
};
There're also some other solutions with side effects. You can declare Object::Object(int) as explicit to prohibit the implicit conversion from int to Object and then make a = 1 fail. But note this will make Object a = 1; fail too because copy initialization doesn't consider explicit constructor. Or you can mark the copy assignment operator deleted too, but this will make the assignment between Objects fail too.
How can I do it?
Option 1:
Make the constructor explicit
struct Object
{
explicit Object(int in) {}
};
Option 2:
delete the assignment operator.
struct Object
{
Object(int in) {}
Object& operator=(int in) = delete;
};
You can use both of the above options.
struct Object
{
explicit Object(int in) {}
Object& operator=(int in) = delete;
};
Option 3:
If you don't want any assignment after initialization, you can delete the assignment operator with Object as argument type.
struct Object
{
explicit Object(int in) {}
Object& operator=(Object const& in) = delete;
};
That will prevent use of:
Object a(1);
a = Object(2); // Error
a = 2; // Error
Deleted functions are available only from C++11 onwards, for older compilers you can make the assignment operator private.
struct Object
{
Object(int) {}
private:
Object& operator=(int);
};
Compiler will now throw error for
Object a=1; //ok
a=2; // error
But you can still do
Object a=1,b=2;
b=a;
Because the default assignment operator is not prevented from being generated by the compiler. So marking default assignment private will solve this issue.
struct Object
{
Object(int) {}
private:
Object& operator=(Object&);
};

default copy assignment operator doesn't pass the is_copy_assignable test

#include <type_traits>
struct A {};
struct B {
B(A& a) : a(a) {}
//B& operator=(B const& b) = default; // LINE 1
//B& operator=(B const& b) { a = b.a; return *this; } // LINE 2
A& a;
};
static_assert(std::is_copy_constructible<B>::value, ""); // pass
static_assert(std::is_copy_assignable<B>::value, ""); // fail
int main() {
A a;
B b(a);
return 0;
}
Compiler: g++ 5.1
The default copy constructor is fine, but the default copy assignment operator fails (even with LINE 1 uncommented).
Declaring it explicitly (uncommenting LINE 2) works though. Is it a bug?
No, it's not a bug. Your class doesn't have a defined copy assignment operator, because you have a reference member. If you have a class with reference semantics, it is not obvious whether you would want assignment to just assign from one reference to another (which results in copy assigning the referenced object), or whether the reference should be rebound (something you can't actually do, although this is usually what you would want). So the standard simply doesn't generate a default assignment operator, but allows you to define one manually with the semantics you want.

Behaviour of copy constructor accepting const reference

In the following Code, in this line
A(A& b)
When using this compiler gives error as
c110.cpp:41: error: no matching function for call to ‘A::A(A)’
c110.cpp:8: note: candidates are: A::A(A&)
But as soon as i convert it into
A(const A& b)
Many many thanx in Advance
No error comes. Why is it so?
Code
class A
{
public:
static int cnt;
A(A& b)
{
cnt++;
cout<<"cnt="<<cnt<<endl;
}
A()
{
cnt++;
cout<<"cnt="<<cnt<<endl;
}
~A()
{
cnt--;
cout<<"cnt="<<cnt<<endl;
}
};
int A :: cnt=0;
A fun(A b)
{
return b;
}
int main()
{
A a;
A b=fun(a);
return 0;
}
Non-const references cannot bind to temporaries. If you pass a temporary as parameter, A& is illegal but const A& isn't.
The line
A b=fun(a);
does copy-initialization on the object returned by fun(a), which is a temporary.
Also, the copy constructor shouldn't take a non-const reference because, logically, you don't need to modify the object you're copying from.
I think its always safe to use A(const A&) type of syntax for copy construction rather than A(A&), because RVO may take place or not, its compiler dependent.
As in the above question RVO is not taking place and the temporary is been created, hence A(const A&) is safe to use.