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

C++ copy, move constructors

我这里有代码:

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

需要移动构造函数,但已被删除。我一直认为移动和复制构造函数在编译方面是等价的,因为右值可以绑定到 const 引用,但是具有显式定义的右值引用的重载只是在重载决策中优先考虑。这是我第一次看到编译器实际上需要 move constuctor 而不仅仅是使用 copy。为什么?我错过了什么吗?

I always thought that move and copy constructors are equivalent in terms of compilation cause rvalue can be bound to const reference, but overload with explicitly defined rvalue reference is just prioritized in overload resolution.

你这个假设是正确的。如果你有一个重载集,它需要一个 const& 和一个 && 如果你有一个右值,那么 && 版本将比 const& 更受欢迎。这里的问题是您的 && 版本被标记为已删除。这意味着您明确声明您不想从临时构造,代码将无法编译。

在这种情况下,如果您不想让 class 移动,那么您可以去掉

Test(Test&&) = delete;

由于您的用户定义的复制构造函数的存在将删除编译器自动生成的移动构造函数。

您显式声明了移动构造函数并将其标记为delete,它将被重载决议选择,然后导致错误。

If both copy and move constructors are provided and no other constructors are viable, overload resolution selects the move constructor if the argument is an rvalue of the same type (an xvalue such as the result of std::move or a prvalue such as a nameless temporary (until C++17)), and selects the copy constructor if the argument is an lvalue (named object or a function/operator returning lvalue reference).

请注意,deleted implicitly-declared move constructor 会被重载决议忽略,但 显式 声明的不会。

The deleted implicitly-declared move constructor is ignored by overload resolution (otherwise it would prevent copy-initialization from rvalue). (since C++14)