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

时间:2019-05-10 05:38:09

标签: c++ gcc

以下代码可以正常工作:

mkr.sumsetting <- function(...,dataset){

  subvars <- rlang::enquos(...)
  #print(subvars)

  # Summarize the variables belonging to the interessting setting
  dfplot <- dataset %>%
    dplyr::select(day,N,!!! subvars) %>%
    dplyr::mutate(s1 = rowSums(!!! subvars,na.rm = TRUE))

  return(dfplot)
   }

结果:

#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;
}

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

sample: 0x1470c20
 oops!
destroy sample

gcc给出了编译错误:

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

这与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) ^ 有什么关系吗? 如何在不定义这些构造函数或不使用显式构造函数的情况下进行编译?

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

-fno-elide-constructors

2 个答案:

答案 0 :(得分:17)

sample s = new oops;

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

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


  

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

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

sample s { new oops };

或者,使用C ++ 17。

答案 1 :(得分:6)

为什么无法编译?

这行代码:

sample s = new oops;

等同于写作:

sample s = sample(new oops);

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

您可以通过将初始化更改为任一来解决此问题

sample s { new oops };

sample s ( new oops );

或者,如果您真的想使用=,则可以利用temporary lifetime extension进行书写

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

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

C ++ 17对所属的值类别集合进行了一些更改。在C ++ 17中,sample(new oops)成为prvalue,并且要求编译器满足prvalue的c ++ 17标准要求到位而不复制或移动它们。这是通过黑暗魔法和法术的结合来完成的。

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

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

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