需要移动构造函数,但未使用,在数组初始化中使用已删除复制构造函数的类元素

Mat*_*usz 3 c++ move c++11

我为冗长的标题道歉,但我无法简洁地表达这个问题。

我有一个带有deleted 复制构造函数和复制赋值运算符的类。当我尝试使用类的实例初始化数组时,除非我提供移动构造函数,否则我会收到编译器错误。但是,提供的移动构造函数实际上并未被调用。

这是一个 MWE 说明了这个问题。

#include <cstdio>
#include <string>

class A
{
public:
    explicit A(std::string s) : s_(s) { puts("constructor"); }
    A() = delete;
    ~A() = default;
    A(const A &other) = delete;
    A &operator=(const A &other) = delete;
    // A(A &&other) : s_(std::move(other.s_)) { puts("move constructor"); }

    void print_s() { printf("%s\n", s_.c_str()); }

private:
    std::string s_;
};

int main()
{
    A arr[]{A{"some"}, A{"string"}};
    for (auto &a : arr) {
        a.print_s();
    }
}
Run Code Online (Sandbox Code Playgroud)

如图所示注释掉移动构造函数,我收到错误消息:

> g++ file.cc -g -std=c++11 -o file && ./file
file.cc: In function ‘int main()’:
file.cc:22:32: error: use of deleted function ‘A::A(const A&)’
   22 |  A arr[]{A{"some"}, A{"string"}};
      |                                ^
file.cc:10:2: note: declared here
   10 |  A(const A &other) = delete;
      |  ^
file.cc:22:32: error: use of deleted function ‘A::A(const A&)’
   22 |  A arr[]{A{"some"}, A{"string"}};
      |                                ^
file.cc:10:2: note: declared here
   10 |  A(const A &other) = delete;
      |  ^
Run Code Online (Sandbox Code Playgroud)

如果我取消注释移动构造函数,我会得到输出

constructor
constructor
some
string
Run Code Online (Sandbox Code Playgroud)

因此,实际上似乎并未调用移动构造函数。

为什么需要移动构造函数?我是不是在某处犯了错误(例如,一个实现不佳的移动构造函数)?

Nat*_*ica 6

A arr[]{A{"some"}, A{"string"}};
Run Code Online (Sandbox Code Playgroud)

您正在创建两个临时A对象,然后将它们复制/移动到数组中。现在,编译器可以优化此复制/移走,但该类仍然需要可复制/可移动才能发生这种情况。当您注释掉移动构造函数时,该类不再是可复制或可移动的,因此您会收到编译器错误。

应该注意的是,这在 C++17 中改变了,它有保证的复制省略。对于无法复制或移动的类型,代码将在 C++17 及更高版本中编译。


如果你改变

explicit A(std::string s) : s_(s) { puts("constructor"); }
Run Code Online (Sandbox Code Playgroud)

A(std::string s) : s_(s) { puts("constructor"); }
Run Code Online (Sandbox Code Playgroud)

那么你的main功能可能会变成

int main()
{
    A arr[]{{"some"}, {"string"}};
    for (auto &a : arr) {
        a.print_s();
    }
}
Run Code Online (Sandbox Code Playgroud)

即使您无法复制或移动A.