test.cpp:
#include <iostream>
class MacroObject {
public :
MacroObject() = default;
MacroObject(const MacroObject&) = delete ;
MacroObject& operator=(const MacroObject&) = delete;
explicit MacroObject(MacroObject&&) = default;
MacroObject& operator=(MacroObject&&) = default;
int init(){return 0;}
int get(){return 0;}
};
MacroObject getObj(){
MacroObject obj;
obj.init();
return obj;
}
int main(){
MacroObject obj{getObj()};
std::cout << obj.get() << std::endl;
return 0;
}
I use this command with g++ 4.8.5:
g++ -std=c++11 test.cpp
I get this error message:
test.cpp: In function 'MacroObject getObj()':
test.cpp:19:8: error: use of deleted function 'MacroObject::MacroObject(const MacroObject&)'
return obj;
^
test.cpp:6:1: error: declared here
MacroObject(const MacroObject&) = delete ;
^
When I remove explicit, it is ok.
Why g++ uses the deleted copy-constructor and not the move-constructor?
By making the move constructor explicit you can't do
MacroObject x;
MacroObject y = std::move(x);
but can do
MacroObject y(std::move(x));
The return from the function, even in newer C++ versions where mandatory copy/move elision (NRVO) is in effect, tries to match the top version. Since that isn't a match, it checks the copy constructor. Since that is deleted, it stops with a failure.
It does not go on to try explicit versions. They are not candidates.
Making the copy and move constructors explicit makes the class useless with many standard library classes / functions so I recommend not making them explicit.
Related
Here's the code I have:
#include <iostream>
#include <functional>
struct Test {
struct Envelope {
const int x = 1;
int y = 2;
int z = 3;
};
Envelope mEnvelope;
struct Buffer {
Envelope mEnvelope;
} mBuffer;
std::function<Buffer()> func{[this] {
mBuffer.mEnvelope = mEnvelope;
return mBuffer;
}};
};
int main() {
Test test;
}
it says:
g++ -std=c++17 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In lambda function:
main.cpp:17:29: error: use of deleted function 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)'
17 | mBuffer.mEnvelope = mEnvelope;
| ^~~~~~~~~
main.cpp:5:12: note: 'Test::Envelope& Test::Envelope::operator=(const Test::Envelope&)' is implicitly deleted because the default definition would be ill-formed:
5 | struct Envelope {
| ^~~~~~~~
main.cpp:5:12: error: non-static const member 'const int Test::Envelope::x', can't use default assignment operator
I've tried using a Copy Constructor:
Envelope(const Envelope &other) {
y = other.y;
}
or override the operator =
Envelope &operator=(Envelope &other) {
// self-assignment guard
if (this == &other) {
return *this;
}
y = other.y;
}
But the errors grown even more.
I need to copy only some "part" of the object.
This is just a test, of course the real object have lots of members fields, and some need to be ignored.
How to do it within a std::function<Buffer()>?
OK, since it might not be obvious what the problem is:
A copy assignment operator should usually have a const& parameter, not a & parameter.
You should then also provide the copy constructor of Envelope as you showed. First of all it has different behavior than the implicitly-generated one (which would copy over all the elements, not only y) and the generation of the implicit copy constructor when there is a user-defined copy assignment has been deprecated since C++11 and will probably be removed in a future standard iteration.
Then you will need to default the default constructor as well, since you have a user-defined constructor now:
Envelope() = default;
Furthermore, your copy assignment operator is declared to return a Envelope& (as it should), but you forgot to actually put a return statement in it at its end, so executing it will cause undefined behavior as is:
return *this;
You can create your own copy ctor/operator in order to only copy the info you want:
#include <iostream>
#include <functional>
struct Test {
typedef struct {
const int x;
int y;
int z;
} Envelope_t;
public:
Test():env({1,2,3}){}
Test(const Test & copy):env({copy.env.x,5,copy.env.z}) {}
Test& operator=(const Test& copy){
env.y=copy.env.y+7;
env.z=copy.env.z;
return *this;
}
void printValues() {
std::cout << "x:" << env.x << "\ny:" <<
env.y << "\nz:" << env.z << "\n\n";
}
private:
Envelope_t env;
};
int main() {
Test original;
original.printValues();
Test copyCtor(original);
copyCtor.printValues();
Test copyOp;
copyOp = copyCtor;
copyOp.printValues();
}
My confusion lies at line Cube c = a;. I think it should call both default constructor and copy constructor, but in fact it only calls copy constructor.
Isn't Cube c, just like Cube a, a new object that should invoke default constructor?
class Cube
{
public:
int length_;
Cube(){
length_ = 1;
cout<< "Default Constr"<< endl;
}
Cube(const Cube & obj){
length_ = obj.length_;
cout<< "Copy Constr"<< endl;
}
};
Cube foo(){
Cube c;
return c;
}
int main() {
Cube a; //invoke default constructor only
Cube c = a; //invoke copy constructor only
return 0;
}
As others have pointed out, what you have here is copy initialization, so there's no way a default constructor would be used.
There is a slightly different case in which copy initialization can (at least theoretically) involve an extra step. Consider code like this:
class foo {
public:
foo(int) {}
};
int main() {
foo f = 1;
}
In this case, (at least before C++17) there were theoretically supposed to be two separate constructors involved. First, a temporary was constructed, initialized with 1, then the copy constructor was called to initialize f from that temporary object.
In this case, most compilers will generate code that just directly initializes f from q, so it's equivalent to foo f{1};. The compiler is still required to respect the fact that a copy is needed though, so if you delete the copy constructor, compilation will fail:
class foo {
foo(foo const &)= delete;
public:
foo(int) {}
};
int main() {
foo f = 1;
}
Result (with gcc):
trash9.cpp: In function 'int main()':
trash9.cpp:8:17: error: use of deleted function 'foo::foo(const foo&)'
foo f = 1;
^
trash9.cpp:2:9: note: declared here
foo(foo const &) = delete;
^~~
trash9.cpp:4:9: note: after user-defined conversion: 'foo::foo(int)'
foo(int) {}
^~~
But changes in the rules starting with C++17 mean that now even that is allowed (so if I add -std=c++17 to the compilation above, it succeeds).
When you write:
Cube c = a;
This is actually not an assignment but a copy-initialization.
In your case, it would be the same as if you had written:
Cube c(a);
Only the copy-constructor is called.
Keep in mind that an object is constructed only once, therefore only one constructor.
The default constructor is called.
It is used to initialize the object.
Then the copy constructor is used to set the state of the new object to be the same with the initial one.
While testing some classes I run into an interesting problem: When invoking class constructor using the equals sign notation (=) if the copy constructor is deleted an error is encountered error: copying variable of type 'Class' invokes deleted constructor. While using the parenthesis the code compiles normally.
What is going on here? Can this be a compiler bug?
Consider the following class:
class Test
{
public:
int Int;
public:
Test() = default;
Test(Test &) = delete;
Test(Test &&) = delete;
Test(int i)
{
Int = i;
}
};
Constructors invoked like so:
Test t1(3); //No error
Test t2 = 3; //error: copying variable of type 'Class' invokes deleted constructor
Just to check I tried to add some checks and allow these functions and compile the code.
Both constructors compiled using MSVC in the exact same way.
class Test
{
public:
int Int;
public:
Test()
{
Int = 0;
cout << "Constructor";
}
Test(Test &t)
{
Int = t.Int;
cout << "Copy Constructor";
}
Test(Test &&t)
{
Int = t.Int;
cout << "Move Constructor";
}
Test(int i)
{
Int = i;
cout << "Constructor from int";
}
};
Test t1(3); //Constructor from int
Test t2 = 3; //Constructor from int
What exactly is going on here?
You're seeing the results of the copy elision rule.
Basically, saying T var = expr; constructs an unnamed temp from expr and then copies or moves it into var using the copy or move construtor. If the copy and move constructors are deleted, then this gives an error about the deleted constructor. But then, the compiler is required to elide that copy or move and construct var directly from expr, even if the copy or move constructor has visible side effects. Its one of those weird corner cases that arises from language design by de-facto standardization of what disparate implementations do (or did at some time in the past), plus design-by-committee and slow evolution over time while trying to keep backwards compatibility.
see here for more discussion
The following code fails with gcc 4.8.0 (mingw-w64) with -O2 -std=c++11 -frtti -fexceptions -mthreads
#include <string>
class Param
{
public:
Param() : data(new std::string) { }
Param(const std::string & other) : data(new std::string(other)) { }
Param(const Param & other) : data(new std::string(*other.data)) { }
Param & operator=(const Param & other) {
*data = *other.data; return *this;
}
~Param() {
delete data;
}
Param & operator=(Param &&) = delete;
private:
std::string * data;
};
int main()
{
Param param;
param = Param("hop");
return 0;
}
With the error : error: use of deleted function 'Param& Param::operator=(Param&&)'
On the line :
param = Param("hop");
And compiles well if I remove the move assignment delete line.
There should be no default move assignment operator since there are user defined copy constructors, user defined copy assignment, and destructors, so deleting it should not affect the compilation, why is it failing?
And why is the allocation simply not using a copy assignment?
The function you deleted is exactly the assignment operator you try to use in main. By explicitly defining it as deleted you declare it and at the same time say using it is an error. So when you try to assign from an rvalue (Param("hop")), the compiler first looks whether a move assignment operator was declared. Since it was and is the best match, it tries to use it, just to find that it was deleted. Thus the error.
Here's another example of this mechanism which uses no special functions:
class X
{
void f(int) {}
void f(short) = delete;
};
int main()
{
X x;
short s;
x.f(s); // error: f(short) is deleted.
}
Removing the deleted f(short) will cause the compiler to select the non-deleted f(int) and thus compile without error.
Here is the situation I came up with:
#include <iostream>
using namespace std;
struct test {
test() { cout << "ctor" << endl; }
test(const test&) = delete;
test(test&&) = delete;
};
auto f() -> test {
return {};
// return test{};
}
auto main() -> int {
f();
}
This code compiles with both clang and gcc, but when I change return {} to return test{} it doesn't compile anymore. Why is that? Shouldn't it work the same in both cases?
Frankly, I don't know if there is a good use case for this, but it caught me by surprise, so now I'm wondering what's going on.
return {} uses an empty initialiser list to initialise the return value, using the default constructor.
return test{} creates a temporary using the default constructor, then uses that to initialise the return value using a move or copy constructor. You have deleted those constructors, so that can't be done.
In practice, the copy or move will be elided so that both have the same effect - but the second still requires an accessible constructor, even if it's not actually used.
As an extension of Mike's answer:
int main()
{
// Error: Call to deleted move constructor (in C++98 the compiler would
// shout at the private copy constructor).
// auto t = test{};
// OK: Default constructor used
test t2;
test t3{};
return 0;
}
Even though the move/copy is elided, the C++ standard requires visibility of these constructors.