为什么删除move构造函数会导致编译错误?

Liz*_*zho 15 c++ gcc

以下代码可以正常工作:

#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;
}
Run Code Online (Sandbox Code Playgroud)

结果:

sample: 0x1470c20
 oops!
destroy sample
Run Code Online (Sandbox Code Playgroud)

它清楚地显示了move和copy构造函数都没有被调用。删除这些构造函数后,

sample(const sample&) = delete;
sample(sample&&) = delete;
Run Code Online (Sandbox Code Playgroud)

gcc给出了一个编译错误:

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)
  ^
Run Code Online (Sandbox Code Playgroud)

这和什么有关-fno-elide-constructors吗?如何在不定义这些构造函数或不使用显式构造函数的情况下进行编译?

编辑:我的GCC版本是5.4.0。该命令是:

g++ bpp.cpp -std=c++17
Run Code Online (Sandbox Code Playgroud)

Dan*_*ica 17

sample s = new oops;
Run Code Online (Sandbox Code Playgroud)

这是复制初始化的一种形式。为了使编译器在C ++ 17之前解决该问题,必须存在一个复制或移动构造函数。但是,由于进行了优化,编译器可以随意取消其调用(使用GCC和-fno-elide-constructors,调用move构造函数)。

从C ++ 17开始,不需要这些构造函数:https : //wandbox.org/permlink/3V8glnpqF5QxljJl


如何在不定义这些构造函数或不使用显式构造函数的情况下进行编译?

很简单,避免复制初始化,而使用直接初始化

sample s { new oops };
Run Code Online (Sandbox Code Playgroud)

或者,使用C ++ 17。


J. *_*rez 6

为什么无法编译?

这行代码:

sample s = new oops;
Run Code Online (Sandbox Code Playgroud)

等同于写作:

sample s = sample(new oops);
Run Code Online (Sandbox Code Playgroud)

在C ++ 11和C ++ 14中,这将隐式调用move构造函数(如果没有可用的move构造函数,则将调用copy构造函数)。因为允许编译器取消副本和移动,所以实际的移动被忽略了,并且在调用move构造函数时不显示任何内容。即使忽略了实际的动作,程序也不允许隐式引用已删除的函数,因此存在编译器错误。

您可以通过将初始化更改为

sample s { new oops };
Run Code Online (Sandbox Code Playgroud)

要么

sample s ( new oops );
Run Code Online (Sandbox Code Playgroud)

或者,如果您确实想使用=,则可以利用生命周期临时延长来编写

// s won't get destroyed until the end of the scope
// it's safe to use s after this statement
sample&& s = new oops;
Run Code Online (Sandbox Code Playgroud)

C ++ 17会发生什么变化?

C ++ 17对属于的值类别对象集进行了一些更改。在C ++ 17中,它sample(new oops)成为prvalue,并且c ++ 17标准要求编译器必须将prvalue保留在适当的位置,而无需复制或移动它们。这是通过黑暗魔法和法术的结合来完成的。

这意味着在c ++ 17 中 sample s = new oops;合法的

为什么添加时仍然无法编译-std=c++17

该代码应在C ++ 17下编译,并且由于gcc 6.3和更早的版本未实现c ++ 17标准的该部分,因此会发生错误。此问题已在gcc 7.1中修复,并且代码将按预期编译。