简体   繁体   中英

Why does deleting the move constructor cause a compile error?

The following code works OK:

#include <iostream>
using namespace std;
struct oops
{
        ~oops()
        {
                cout << " oops! " << endl;
        }
};

struct sample
{
        oops* x = nullptr;
        sample(oops* p) : x(p)
        {
                cout << "sample: " << p << endl;
        }
        ~sample()
        {
                delete x;
                cout << "destroy sample " << endl;
        }
        sample(const sample&)
        {
                cout << "copy sample " << endl;
        }
        sample(sample&&)
        {
                cout << "move sample " << endl;
        }
};

int main()
{
        sample s = new oops;
        return 0;
}

Result:

sample: 0x1470c20
 oops!
destroy sample

It clearly shows that neither the move nor copy constructor was called. When these constructors are deleted,

sample(const sample&) = delete;
sample(sample&&) = delete;

gcc gives a compile error:

bpp.cpp: In function ‘int main()’:
bpp.cpp:29:17: error: use of deleted function ‘sample::sample(sample&&)’
  sample s = new oops;
                 ^
bpp.cpp:24:2: note: declared here
  sample(sample&&) = delete;
  ^
bpp.cpp:14:2: note:   after user-defined conversion: sample::sample(oops*)
  sample(oops* p) : x(p)
  ^

Does this have anything to do with -fno-elide-constructors ? How can I compile it without defining these constructors or using an explicit constructor?

Edit: My GCC verison is 5.4.0. The command is:

g++ bpp.cpp -std=c++17
sample s = new oops;

This is a form of copy initialization . For a compiler to resolve it until C++17, a copy or move constructor must exist. However, a compiler is free to elide its call due to optimizations (with GCC and -fno-elide-constructors , the move constructor is called).

Since C++17, neither of these constructors is required: https://wandbox.org/permlink/3V8glnpqF5QxljJl .


How can I compile it without defining these constructors or using explicit constructor?

Very simply, avoid copy initialization and use direct initialization instead:

sample s { new oops };

Or, use C++17.

Why does this fail to compile?

This line of code:

sample s = new oops;

is equivalent to writing:

sample s = sample(new oops);

In C++11 and C++14, this implicitly calls the move constructor (or the copy constructor, if no move constructor is available). Because compilers are allowed to elide copies and moves, the actual move is elided, and it doesn't show anything when the move constructor is called. Even though the actual move is elided, programs aren't allowed to refer to deleted functions even implicitly , so there's a compiler error.

You can fix this by changing the initialization to either

sample s { new oops };

or

sample s ( new oops );

or, if you really wanna use = , you can take advantage of temporary lifetime extension to write

// s won't get destroyed until the end of the scope
// it's safe to use s after this statement
sample&& s = new oops;

What does C++17 change?

C++17 made some changes to the set of value categories objects fall in. In C++17, sample(new oops) becomes a prvalue, and the c++17 standard mandates that compilers are required to yeet prvalues into place without copying it or moving them. This is done through a combination of dark magic and sorcery.

This means that sample s = new oops; is legal in c++17.

Why does it still fail to compile when you add -std=c++17 ?

This code should compile under C++17 , and the error you're getting occurs because gcc 6.3 and earlier don't implement that part of the c++17 standard. This issue was fixed in gcc 7.1 and the code will compile as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM