This question already has answers here:
What are the rules for automatic generation of move operations?
(2 answers)
How to find out which functions the compiler generated?
(3 answers)
Closed 2 months ago.
The following code doesn't compile -
struct A {
A() = default;
A(const A& other) = delete;
};
int main()
{
auto a = A();
auto u = std::make_unique<A>(std::move(a));
}
While the following does -
struct A {
A() = default;
A(const A& other) = delete;
A(A&& other) = default;
};
int main()
{
auto u = std::make_unique<A>(A());
}
The error I got is call to implicitly-deleted copy constructor.
Im using a blaze compiler for cpp-17.
Why does the first code segment not compile? It shouldn't use the copy contractor, just the move one.
Edit:
Adding A(A&& other) = default; solves the issue.
Does this mean that deleting the copy-contractor also deletes the move-contractor implicitly, and it needs to be added?
By deleting the copy constructor, you also implicitly delete the move constructor (A(A&& other)). If you want to use the first code segment, you must define your own custom move constructor (which will depend on the actual data of A).
Look at the concepts "rule of 5", "rule of 0" and related links (e.g. here) to get a grip on which constructors must be defined.
(by the way, the second code block does not compile on GCC or Clang, as pointed out in the comments. See godbolt. Edit: code in question was changed so this comment is no longer true.)
Related
This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 5 years ago.
I was testing my knowledge in C++ and have encountered a problem. Consider this code:
class A
{
public:
A(int n = 0)
: m_n(n)
{
std::cout << 'd';
}
A(const A& a)
: m_n(a.m_n)
{
std::cout << 'c';
}
A(A&& a){
std::cout<<'r';
}
private:
int m_n;
};
int main()
{
A a = A();
std::cout << std::endl;
return 0;
}
Clearly, the A() constructor is an rvalue, as no permanent object has been created. So I think that first I have to see "d" as output. Then we are copying the rvalue to our new object, which is yet to be initialised. I have implemented a copy constructor that accepts an rvalue as an argument but I did not see the proof (no "r" output).
Can someone explain why that is?
You're seeing a form of copy elision -- the compiler is actually required to optimize and implement A a = A() by initializing a directly with the arguments to the constructor expression (() in this case) rather than creating a temporary and copying it into a. If you want to enforce an unnamed object to be copied, you need to initialize a with an expression that is not a simple constructor call.
Note that even then, the compiler may elide construtor calls by the as-if rule (if the only visible side effects are in the copy/move constructors/destructors. Your best bet to "force" a call to the move ctor is to use a named value and std::move:
A a1;
A a2 = std::move(a1);
Quoted from C++ Primer
if we explicitly ask the compiler to generate a move operation by
using = default, and the compiler is unable to move all the
members, then the move operation will be defined as deleted
the move constructor is defined as deleted if the
class has a member that defines its own copy constructor but does not also
define a move constructor, or if the class has a member that doesn’t define its
own copy operations and for which the compiler is unable to synthesize a move
constructor
Some code seems to violate this rule:
#include <utility>
#include <iostream>
struct X {
X() = default;
X(const X&) { std::cout << "X(const X&)" << std::endl; }
int i;
};
struct hasX {
hasX() = default;
hasX(const hasX &) = delete;
hasX(hasX &&) = default;
X mem;
};
int main()
{
hasX hx, hx2 = std::move(hx); //output is X(const X&)
}
X doesn't define move constructor and compiler can't synthesize one for it.
According to the rule above, hasX's move constructor is deleted.
However, because hasX's copy constructor is deleted, hx2 = std::move(hx) must call move constructor to output "X(const X&)", which shows hasX's move constructor is defined and it use X's copy constructor to "move". This seems to violate the rule above.
So, is't defined in C++ standard or just a compiler implementation?
Compiler I tested: VS2015 and a online compiler
Thank you for help!
Your book seems to be wrong. Copying is a valid move operation so as long as the member has a copy constructor in the form of const type&, so it can bind to rvalues, the move operation will fall back to a copy.
In your example
hasX(hasX &&) = default;
Can be replaced with
hasX(hasX &&rhs) : mem(std::move(rhs.mem)) {}
as that is what the default does and it will compile just fine.
class A
{
int a;
public:
A(const A&) = delete;
A& operator=(const A&) = delete;
A() : a {0}
{ }
};
int main()
{
auto a = A {};
}
The above code does not compiles and i get the following error: C2280 'A::A(const A &)': attempting to reference a deleted function
I am using visual studio 2015 compiler. My understanding is with member initialization syntax compiler should directly use the default constructor which is what happens when there is no auto and in main i use A a{}. So i was wondering what is the deal with auto in this case.
auto a = A {};
is only valid if A is copy constructible or move constructible. The fact that you use auto is irrelevant. The same would be true for
A a = A {};
as well.
Declaring a copy constructor – even a deleted one – inhibits implicit declaration of a move constructor so your type A is neither copy constructible nor move constructible. If you add the line
A(A&&) = default;
to the body of A, the code should compile again.
In practice, the compiler will not actually call any copy or move constructor in this case and simply construct the object right in a. But the language rules demand that it must still reject the code which makes sense because whether a piece of code is allowed or not should not depend on an optional compiler optimization.
This behavior will (most likely) change in C++17.
Your understanding is correct, so let's see what's happening here, one step at a time.
A {};
As you said, member initialization syntax, completely kosher here.
auto a = (expression of some kind)
And then you're constructing auto a. After performing type inference, this becomes equivalent to...
A a = (expression of some kind)
Which looks like a copy constructor. Which you deleted.
you should use auto this way:
auto a = new A();
if you don't want to use auto this is c++11 way:
A a{};
This question already has answers here:
What's the exact semantics of deleted member functions in C++11?
(2 answers)
Closed 8 years ago.
After doing a bit of research, I see that C++11 has a defect with allocators that require the type to be movable/copyable. I'm sure that this is the cause of this problem, however I am confused about the behavior between deleted and not declared move semantics.
I have the following code which fails to compile on both MSVC12 and Clang:
#include <vector>
class Copyable
{
public:
Copyable() = default;
Copyable(Copyable const& other)
: m_int(other.m_int)
{}
Copyable& operator= (Copyable const& other)
{
m_int = other.m_int;
return *this;
}
Copyable(Copyable&&) = delete;
Copyable& operator= (Copyable&&) = delete;
private:
int m_int = 100;
};
int main()
{
std::vector<Copyable> objects;
objects.push_back(Copyable{});
}
This fails to compile on MSVC with:
xmemory0(600): error C2280: 'Copyable::Copyable(Copyable &&)' : attempting to reference a deleted function
And on Clang (live sample):
new_allocator.h:120:23: error: call to deleted constructor of 'Copyable'
In both cases, when I remove the explicitly deleted move construct/assign methods, the code compiles. AFAIK when you declare copy assign/construct methods, the compiler does not implicitly declare the corresponding move members. So they should still be effectively deleted, right? Why does the code compile when I remove the explicit deletion of move construct/assign?
What is a good workaround for this C++11 defect in general? I do not want my objects to be movable (but they are copyable).
Deleting a function is not the same as not declaring it.
A deleted function is declared and participates in overload resolution, but if you attempt to call it an error is produced.
If you fail to declare your move constructor, the compiler will not create one as you created a copy constructor. Overload resolution on an rvalue will find your copy constructor, which is probably what you want.
What you said with your foo(foo&&)=delete was "if anyone tries to move construct this object, generate an error".
I can illustrate the difference here:
void do_stuff( int x ) { std::cout << x << "\n"; }
void do_stuff( double ) = delete;
void do_stuff2( int x ) { std::cout << x << "\n"; }
//void do_stuff2( double ) = delete;
int main() {
do_stuff(3); // works
//do_stuff(3.14); // fails to compile
do_stuff2(3); // works
do_stuff2(3.14); // works, calls do_stuff2(int)
}
the only part with your above problem that makes this a bit more confusing is that special member functions are automatically created or not based off slightly arcane rules.
Copyable(Copyable&&) = delete;
Copyable& operator= (Copyable&&) = delete;
Unless you are an expert in move semantics (and I mean, really knowledgeable), never delete the special move members. It won't do what you want. If you review someone else's code that has done this, call it out. The explanation has to be really solid, and not "because I don't want the type to move."
Just don't do it.
The proper way to do what you want is to simply declare/define your copy members. The move members will be implicitly inhibited (not deleted, but actually not there). Just write C++98/03.
For more details, see this answer.
This question already has answers here:
Why copy constructor not invoked here
(2 answers)
Closed 8 years ago.
I have read some articles concerning this topic but still have problems to compile my own code.
I have class A:
class A
{
public:
List<int> data;
A(){}
A(A&){}
A& operator= (const A& a)
{
// copy the data from a to data
}
};
Class B will call class A:
class B
{
public:
A makeA()
{
A a;
return a;
}
A getA()
{
A a = makeA();
return a;
}
};
When I compile my code with g++ under Linux, I got:
no matching function for call to 'A::A(A)'.
It seems that the compiler has simply ignored the assignment operation. Can you help me out of this?
In order for this to compile, your copy constructor must take its parameter by const reference:
A(const A&){}
Adding const to your constructor signature fixes this problem (demo on ideone).
Since you are defining an assignment operator and a copy constructor, you should strongly consider adding a desctructor ~A() (see the Rule of Three).
The assignment operator is not used here.
A a = makeA();
This line is an initialization; it uses the copy constructor to copy the value returned by makeA into a. The compiler is complaining because A::A(A&) can't be used with a temporary; change it to the much more common form A(const A&) and things will be much better.
#Peter is right. The copy constructor A(A&){} wants to be A(const A&){}, instead. The reason is that A(A&){} tells the compiler to prepare to modify the A passed to the copy constructor, which does not really make sense, and certainly does not make sense in case the A you pass is a temporary.