c++ - 使用 pimpl idiom 移动操作
问题描述
在下面的代码中,我尝试在 PIMPL 惯用语中使用移动赋值,但代码无法编译。
结构.hpp:
#pragma once
#include <memory>
struct A {
std::unique_ptr<struct B> m_x;
A(int x);
~A();
};
结构.cpp:
#include "struct.hpp"
struct B {
int x;
};
A::A(int x) : m_x{new B} { m_x->x = x; }
A::~A() = default;
主.cpp:
#include <utility>
#include "struct.hpp"
int main()
{
A a(2);
A b(3);
a = std::move(b);
return 0;
}
虽然struct.cpp
编译没有警告,但 ```main.cpp`` 没有,给出错误:
$ g++ -c -std=c++17 -o main.o main.cpp
main.cpp: In function ‘int main()’:
main.cpp:8:18: error: use of deleted function ‘A& A::operator=(const A&)’
8 | a = std::move(b);
... (etc) ...
很明显,复制分配A::operator=(const A&)
被删除了,因为它是为 a 删除的std::unique_ptr
。但是为什么编译器首先尝试使用它呢?不应该std::move
强制使用移动分配,它是有效的并为 a 定义的std::unique_ptr
?
解决方案
虽然std::unique_ptr
确实有一个移动赋值运算符,并且想要利用这个事实来使移动赋值看起来很自然A
,但用户声明的构造函数遇到了问题。
移动赋值运算符上的 cppreference :
隐式声明的移动赋值运算符
struct
如果没有为类类型( 、class
或)提供用户定义的移动赋值运算符union
,并且以下所有情况都为真:
- 没有用户声明的复制构造函数;
- 没有用户声明的移动构造函数;
- 没有用户声明的复制赋值运算符;
- 没有用户声明的析构函数,
然后编译器将使用签名将移动赋值运算符声明
inline
为其类的公共成员T& T::operator=(T&&)
。
注意最后一个要点:A
有一个用户声明的析构函数,所以你不会得到隐式声明的移动赋值运算符。
如果我们想以A
最少的努力使移动赋值,我们可以显式声明移动赋值运算符并请求默认实现,如下所示:
结构.hpp:
#include <memory>
struct A {
std::unique_ptr<struct B> m_x;
A(int x);
A& operator=(A&&) noexcept;
~A();
};
结构.cpp:
#include "struct.hpp"
struct B {
int x;
};
A::A(int x) : m_x{ new B } { m_x->x = x; }
A::~A() = default;
A& A::operator=(A&&) noexcept = default;
我们需要在头文件中声明析构函数并移动赋值运算符,但将定义推迟到知道完全定义的B
. 请注意,我手动指定了赋值运算符是noexcept
,因为如果我不在default
声明点创建它,它就不会是noexcept
,隐式声明的移动赋值运算符将是。
推荐阅读
- amazon-web-services - AWS EMR Spark - 从 S3 上的文件中计算数字的平均值
- scala - google-cloud-datastore java 客户端:有没有办法以 Json 的形式推断架构和/或检索结果?
- qgis - 由于编码,LASTools 无法在 WIN 上的 QGIS 3.2 中运行
- swift - 具有 nil 值的 Swift 闭包捕获列表
- swift - Swift 2.2 应用程序不再适用于 macOS 10.14 Mojave
- android - 从异步方法传递 ArrayList
- c++ - 在 lambda 函数中捕获静态成员变量
- jupyter-notebook - Jupyterhub 在登录时创建用户和主页
- android - 如何在特定秒数后显示不同的文本?
- c# - c# DevExpress GridControl