C ++复制,移动构造函数

时间:2018-12-14 14:03:27

标签: c++ c++11 copy-constructor move-constructor

我在这里有代码

#include <string>
#include <iostream>
#include <initializer_list>

template <typename T>
class Test
{
public:
  Test(std::initializer_list<T> l)
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }

  Test(const Test<T>& copy)
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  Test(Test&&) = delete;
  Test() = delete;
};

void f(const Test<Test<std::string>>& x)
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

void f(const Test<std::string>& x)
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
  f(Test<Test<std::string>>{x});
}

int main()
{
  Test<std::string> t1 {"lol"};
  f(t1);
  return 0;
}

我尝试在Linux Mint 19上使用命令GCC 7.3.0对此进行编译:

  

g ++ -std = c ++ 11 -O0 test.cpp -o test -Wall -pedantic

编译失败,并显示以下错误:

f(Test<Test<std::string>>{x});

需要move构造函数,但已删除。我一直认为,移动和复制构造函数在编译方面是等效的,因为可以将右值绑定到const引用,但是在显式解析中优先考虑使用显式定义的右值引用进行重载。这是我第一次看到编译器实际上需要移动构造器,而不仅仅是使用copy。为什么?我想念什么吗?

2 个答案:

答案 0 :(得分:5)

您显式声明了move构造函数并将其标记为delete,它将由重载解析选择,然后导致错误。

  

如果同时提供了copy和move构造函数,并且没有其他可行的构造函数,则当参数为相同类型的右值(如std :: move {{1}的结果的xvalue)时,重载分辨率将选择move构造器。 }),如果参数是左值(命名对象或返回左值引用的函数/运算符),则选择复制构造函数。

请注意,deleted implicitly-declared move constructor会被重载分辨率忽略,但明确声明的不会。

  

已删除的隐式声明的move构造函数将被重载解析忽略(否则它将阻止rvalue的复制初始化)。 (自C ++ 14起)

答案 1 :(得分:4)

  

我一直认为移动和复制构造函数在编译方面是等效的,因为可以将右值绑定到const引用,但是在显式解析中优先考虑使用显式定义的右值引用进行重载。

您对此假设是正确的。如果您有一个包含const&&&的重载集,那么如果您有右值,则&&的版本将比const&更好。这里的问题是您的&&版本被标记为已删除。这意味着您明确表示您不想从临时结构进行构造,并且代码将无法编译。

在这种情况下,如果您不希望班级移动,那么就可以摆脱

Test(Test&&) = delete;

由于用户定义的副本构造函数的存在,将删除编译器自动生成的move构造函数。