2016-09-26 106 views
17

Herb Sutter's talk at CppCon16中,他建议用const std::unique_ptr(大约10分钟)写作pimpl成语。使用const std :: unique_ptr获取pimpl成语

这应该如何与移动构造函数/作业?在C++ 17中有什么吗?我找不到任何东西。

+0

没有读过这篇演讲,很明显pimpl的类不可复制/移动。它们将在动态范围内实例化,并通过智能指针完全访问,pimpl隐藏了内部实现细节。 –

+6

为什么你不希望你的大多数pimpl类可移动?这似乎是一个完全合理的事情。 –

+1

@DenisYaroshevskiy只能假设他有一个特定的用例。一般来说,我会同意使用'unique_ptr'作为pimpl的容器。如果你希望它是可复制的,那么当然你需要用克隆操作来实现它。 –

回答

2

这是假设如何使用移动构造函数/赋值?

Move constructors

的隐式或声明为已删除如果任一下列条件为真被定义为T类默认的移动构造函数:

  • T具有非无法移动的静态数据成员(已删除,无法访问或含糊不清的移动构造函数)

const std::unique_ptr是这样一个数据成员,因为const

如果const被丢弃,编译器会生成移动构造函数和赋值,但不会生成复制的构造函数和赋值。


草本解释了为什么他使用const unique_ptr

非const可以工作过,但它是更脆弱,因为默认情况下移动语义可能是不正确。

使用const成员,它更健壮,因为const成员必须在构造函数中初始化。而const文件表示该对象的执行没有改变,它不是状态或策略设计模式。

+0

然后在这种情况下,人们将不得不实施复制构造函数? – vordhosbn

+0

@vordhosbn如果这是必要的。 –

+0

这是真的,但我的问题是关于移动建设/任务。使用简单的unique_ptr而不是const将保持移动构造/赋值活着,同时声明它为const禁止它。 我曾经认为const数据成员是一个不好的习惯(因为这个问题),但显然Herb Sutter不这么认为。我想知道为什么。 –

8

如果你的类不应该是空的,那么非const唯一的ptr(带有默认的移动/赋值)是不合适的。移动ctor和移动分配都将清空rhs。

一个const unique ptr将禁用这些自动方法,如果你想移动,你将不得不在impl(和一些外部的胶水)内写入它。

我会亲自用我想要的语义编写一个值ptr(然后让编译器编写胶水),但是以const unique_ptr开头听起来合理,因为第一遍是合理的。

如果你放松了永不消逝的空间,并且几乎从不空虚,那么你现在必须推理许多方法的先决条件,以及可能发生的连锁错误。

这种技术的最大代价是返回值的困难,C++ 17消失了。

+2

如果你从一个对象中移出它假设处于部分形成状态,所以它只能被分配和析构,所以移动构造器/分配不会破坏你的not_null保证。如果你愿意的话,你可以拿出来像gn的not_null一样包装它。 –

+0

@DenisYaroshevskiy不,它处于你愿意保证的状态?它不是“应该处于部分形成的状态”,除非这是你*选择*保证的。一种选择是几乎不会空的,并且一旦移动就只能是空的。这是一个选择,它有成本和收益。不应该忽视从不空虚到几乎不空虚的成本。同样是从几乎从不空虚到永不空虚的成本。永不空白更容易*保证正确性,并且通过更快速的代码偏向更经常正确的代码通常是一个好主意。 – Yakk

+0

@Yakk即使移动对象是“从不空”,移动后使用它是一种强烈的代码味道。我认为pimpl指针检查后跟assert /异常优于允许人们依赖于处于“默认”状态的移动对象。当由于不可避免的使命蔓延而无法保证担保时,这是一场灾难。我会尽量说这是一种与“std :: move”精神不符的反模式。 –