I have a class definition. And I am confused about some constructor behaviour. Following is the code.
#include <iostream>
#include <cstdlib>
using namespace std;
class A
{
int i;
public:
void seti(int v) {cout << "Setting i\n"; i=v;}
void print() {cout << "i = " << i << endl;}
A(){};
A(int v) {cout << "Setting i in par-ctor to " << v << "\n"; i=v;}
A(A& o) {cout << "In copy ctor, changing i to " << o.i << "\n";i=o.i;}
A& operator=(A o) {cout << "In assignment op\n"; this->i = o.i; return(*this);}
};
int main()
{
A o1;
A o2(2);
A o3 = A(4);
A o4 = 35;
return(0);
}
I want to know why this code does not compile unless
a) The defined copy constructor is commented, or
b) The defined copy constructor has a 'const' qualifier as in A& A(const A& o) or
c) Object initializations for both o3 and o4 are removed.
Regarding (c) in which the invocation of constructor A(int) is expected,
How does the actual copy constructor definition (the one without const) conflict with the parametrized (with int) constructor ?
First look here:
A o3 = A(4);
The A(4) creates a temporary object. This expression is an rvalue. An rvalue cannot bind to a non-const lvalue reference like A&, so the copy constructor cannot be chosen. A better copy constructor declaration has a const A&, so that it can be constructed from rvalues too. That's why your (b) fix works.
The same problem manifests here:
A o4 = 35;
In copy-initialisation (with an =), a temporary of the object is constructed and then copied into the object you're declaring. So this is equivalent to:
A o4 = A(35);
And therefore exhibits the same problem as above.
Related
I have the following code, which apparently compiles in MSVC and GCC:
#include <iostream>
class Test
{
public:
Test() = default;
Test(Test const& other) = delete;
Test(Test&& other) noexcept = delete;
Test& operator=(Test const& other) = delete;
Test& operator=(Test&& other) = delete;
auto getX() -> int
{
return x;
};
auto setX(int xz) -> void
{
x = xz;
};
private:
int x = 42;
};
void something(Test&& thing) {
thing.setX(44);
std::cout << thing.getX() << std::endl;
}
int main()
{
Test a;
a.setX(3);
std::cout << "Before call: " << a.getX() << std::endl;
something(std::move(a));
std::cout << "After call: " << a.getX() << std::endl;
}
Now, I would have expected this to not compile. Both the move and the copy constructor of Test are deleted. The function something only accepts r-value references. However, it does compile, and this is the output of the program:
Before call: 3
44
After call: 44
The only way that I could think of how it should be possible to pass an object of type Test to a function is by using an l-value reference or const ref. But the way something is defined, it should only accept rvalues, shouldn't it?
Here is the code on compiler explorer: https://godbolt.org/z/rGKdGbsM5
rvalue references are just references just like lvalue references. The difference between them is the kind of expression that can be used to initialize them.
std::move casts an lvalue reference to an rvalue reference. It doesn't require the type to be movable. If you try to actually move thing in something then you will get errors.
In my buggy class, I'm only exposing a constant version of MyStruct, experimenting with initializing a constant reference to the private member in an initializer list.
The program I'm debugging is functionally equivalent to the following:
#include <iostream>
#include <optional>
struct MyStruct {
int member = 1;
};
class MyType {
MyStruct struct_member_;
public:
MyType() : struct_member(struct_member_) {}
MyType& operator =(const MyType& other_side) {
struct_member_ = other_side.struct_member_;
return *this;
};
const MyStruct& struct_member;
void test() const {
std::cout << "Why isn't " << &struct_member << " the same as " << &struct_member_ << std::endl;
}
};
int main()
{
std::optional<MyType> my = MyType();
my.value().test();
std::optional<MyType> yours = MyType();
yours.value().test();
my = yours;
my.value().test();
my = MyType();
my.value().test();
return 0;
}
For this program, here's the output:
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
Why isn't 0x7ffeefbff868 the same as 0x7ffeefbff878
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
Why isn't 0x7ffeefbff890 the same as 0x7ffeefbff8a0
However, in the program I'm debugging, struct_member_ and struct_member are getting desynchronized (either the const reference struct_member is getting assigned to a different place in memory-- except it's const, or it's loosing track with an updated struct_member_-- except its memory address shouldn't change?) and I'm not sure which or why.
Any ideas on what could lead to this happening, or tips to make this pattern work with non-POD types? (I plan to be transitioning to method-based accessors anyway, since this experiment seems to be failing.)
Turns out optionals and copy constructors were key to all of this. The copy constructor was defaulting and I didn't realize it. Funny, because the compiler warned that I needed a non-default operator=, but didn't afford me the same warning for the copy constructor.
class MyType {
MyStruct struct_member_;
public:
MyType() : struct_member(struct_member_) {
std::cout << "constructor" << std::endl;
}
MyType(const MyType& other) : struct_member(struct_member_) {
std::cout << "copy constructor " << std::endl;
struct_member_ = other.struct_member_;
};
MyType& operator =(const MyType& other_side) {
std::cout << "assignment operator called" << std::endl;
struct_member_ = other_side.struct_member_;
return *this;
};
const MyStruct& struct_member;
void test() const {
std::cout << "Why isn't " << struct_member.member << ": " << &struct_member << " the same as " << &struct_member_ << std::endl;
}
};
Yields the correct output:
constructor
copy constructor
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
constructor
copy constructor
Why isn't 0x7ffeefbff878 the same as 0x7ffeefbff878
assignment operator called
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
constructor
assignment operator called
Why isn't 0x7ffeefbff8a0 the same as 0x7ffeefbff8a0
Altogether, it'd be less hassle just to expose getters as methods.
Either delete the copy constructor (because the default behavior binds the reference to the "wrong" thing in this case):
MyType(MyType const&) = delete;
Or provide a copy constructor that does the right thing, by binding the public const accessor to the self-same object:
MyType(MyType const& other)
: struct_member_{other.struct_member_}
, struct_member(struct_member_) {}
So I am new to move semantic and I am testing out the following code. My understanding is that rvalue will invoke the move constructor and I expected A("123") will cause the move constructor to be called. But when I ran this, the copy constructor is called instead.
#include <string>
#include <iostream>
#include <utility>
class A
{
std::string s;
public:
A(const std::string& in) : s(in) { std::cout << "ctor!\n";}
A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
A(A&& o) noexcept : s(std::move(o.s)) { }
};
class B
{
A d_a;
public:
B(const A& a) :d_a(a)
{}
};
int main()
{
std::cout << "Trying to move A\n";
B b(A("123")); // move-constructs from rvalue temporary
}
The issue is the constructor of B:
B(const A& a) :d_a(a) {}
The function parameter const A& is a const-qualified lvalue reference, which you can't cast to an rvalue. You need to change the constructor to (or add a second one)
B(A&& a) : d_a(std::move(a)) {}
As a side note, you get the correct move semantics for the types in your example for free, if you just define them as
struct A {
std::string s;
};
struct B {
A d_a;
};
with the client code
B b{A{"123"}};
I understand that you wanted to not rely on compiler-generated special member functions for the sake of investigating the move-construction, I just didn't want to omit this shortcut, because this is the setup one should strive for: let the copy and move semantics of your class be automatically assembled by their data members.
A const lvalue reference binds to anything. Your B has a constructor that takes A by a const lvalue reference. When you pass a temporary A to this constructor, this will eventually lead to some parameter setting that looks like
const A& tmp = temporary_A;
and from then on, your A will be treated as a const lvalue reference which makes a call to copy constructor since that matches the signature. You need to define a constructor in B that takes A by an rvalue reference.
B(A&& a)
As things currently are, you will see a print out like:
Trying to move A
ctor!
move failed!
ctor! is printed when constructing the temporary and since your parameter is taken as const lvalue reference d_a(a) calls the copy constructor. Below, modified code avoids copy.
#include <string>
#include <iostream>
#include <utility>
class A
{
std::string s;
public:
A(const std::string& in) : s(in) { std::cout << "ctor!\n";}
A(const A& o) : s(o.s) { std::cout << "move failed!\n"; }
A(A&& o) noexcept : s(std::move(o.s)) { }
};
class B
{
A d_a;
public:
B(A&& a) :d_a(std::move(a))
{}
};
int main()
{
std::cout << "Trying to move A\n";
B b(A("123")); // move-constructs from rvalue temporary
}
Here is the code: http://coliru.stacked-crooked.com/a/f7731b48747c61a9
#include <iostream>
struct A{
A(const A&){ std::cout << "A(const A&)" << std::endl;}
A(const A&&){ std::cout << "A(const A&&)" << std::endl;}
A(){ }
};
A foo(){
return *new A;
}
int main()
{
A a;
A c(foo());
}
Since, I passed to the c's constructor argument a temporary object, I expected the move constructor to be called. But the copy constructor was. Why?
Since foo returns a non-constant rvalue, it cannot bind to the const-rvalue reference. The copy constructor is the only remaining viable overload.
A constructor that would be a better overload than the copy constructor would be:
A(A&&);
Additionally, either constructor (copy or move) may be elided under the rules of copy elision (e.g. see here or here.)
I have compiled this code with vs2011. It prints first constructor then copy constructor.
But if I change the function to return a instead of ap, it will move the object. Is this a bug or why does it behave like this? Is *ap not a rvalue?
struct A
{
A() { cout << "constructor" << endl;}
A(const A&) { cout << "copy constructor " << endl;}
void operator=(const A&) { cout << "assignment operator" << endl; }
A( A&&) { cout << "move copy constructor" << endl;}
void operator=(A&&) { cout << "move assignment operator" << endl;}
};
A func() { A a; A *ap = &a; return *ap; }
int main()
{
A a = func();
return 0;
}
*ap is an lvalue (ยง 5.3.1.1, n3290) which is in general not safe for the move to happen automatically. The local variable return a; is a different case. There's no requirement for the compiler to prove that in this specific instance it would be safe. This is another good reason for not using pointers in cases where you don't really want pointer semantics.
Changing it to:
return std::move(*ap);
will cause it to be explicitly moved however.