【问题标题】:Non-copyable deleter in std::unique_ptrstd::unique_ptr 中的不可复制删除器
【发布时间】:2014-06-30 01:20:38
【问题描述】:

我一直在寻找另一个问题 (this one) 的解决方案。似乎应该可以为可以解锁互斥锁的unique_ptr 实例创建自定义删除器。

例如,

#include <mutex>
#include <memory>

struct LockDeleter
{
  std::unique_lock<std::mutex> lock_;

  LockDeleter(std::mutex& m) : lock_(m) {}
  void operator()(void*) { lock_.unlock(); }
};

int main()
{
  std::mutex moo;

  {
    std::unique_ptr<int, LockDeleter> ptr(new int(42), LockDeleter(moo));
  }
}

在VS2013 express下编译,我得到

错误 1 ​​错误 C2280: 'std::unique_lock::unique_lock(const std::unique_lock &)' : 试图引用已删除的函数

此诊断发生在编译器生成的函数“LockDeleter::LockDeleter(const LockDeleter &)”中

现在,我似乎无法强制移动锁定删除器实例,而不是使用std::move 复制...即使向LockDeleter 添加显式移动构造函数也不会帮助,被删除的复制构造函数仍然被调用。

那么,我是在做一些傻事,还是必须 unique_ptr 删除器始终是可复制构造的?

【问题讨论】:

  • 似乎是编译器错误; gcc 和 clang 都可以。
  • @ecatmur:很有趣。在我的实验过程中,我注意到 vs2013 也不支持 std::bind 移动参数值(参见示例 here)所以我猜 VS2013 在不可复制对象方面存在多个微妙问题。
  • @ecatmur:确实,启动我的一些笨拙的旧 debian VM 表明 GCC 4.6 和 4.8 似乎都很乐意编译这段代码。随意发布这个小评论作为答案。
  • VS2013 RTM 不会生成隐式移动成员(CTP 版本会生成)。您需要自己实现移动构造函数。编辑:does not seem to help. 一定是标准库错误?
  • 挖掘 VS12 的 &lt;memory&gt;unique_ptr 的构造函数将删除器参数按值传递给基类,因此(错误地)实例化删除器的复制构造函数。

标签: c++ c++11 move unique-ptr


【解决方案1】:

unique_ptr 需要支持移动构造删除器; 20.7.1.2.1 [unique.ptr.single.ctor]

9 - [...] 如果 [deleter type] D 是非引用类型 A,那么 [two-argument constructor] 签名是:

unique_ptr(pointer p, const A& d);
unique_ptr(pointer p, A&& d);

[...]

12 - 要求: [...]

  • [如果d 是一个非常量右值则]D 应满足MoveConstructible 的要求(表20),并且D 的移动构造函数不应抛出异常。这个unique_ptr 将保存从d 构造的值移动。 [...]

如果我们显式地添加一个移动构造函数并从LockDeleter 中删除复制构造函数,那么我们会得到一条信息更丰富的错误消息; http://rextester.com/XFYUG91939:

Error(s):
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1243) : error C2280: 'LockDeleter::LockDeleter(const LockDeleter &)' : attempting to reference a deleted function
        source_file.cpp(10) : see declaration of 'LockDeleter::LockDeleter'
        C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1241) : while compiling class template member function 'std::_Unique_ptr_base<_Ty,_Dx,false>::_Unique_ptr_base(int *,_Dx)'
        with
        [
            _Ty=int
,            _Dx=LockDeleter
        ]
        C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1380) : see reference to function template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,false>::_Unique_ptr_base(int *,_Dx)' being compiled
        with
        [
            _Ty=int
,            _Dx=LockDeleter
        ]
        C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\INCLUDE\memory(1331) : see reference to class template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,false>' being compiled
        with
        [
            _Ty=int
,            _Dx=LockDeleter
        ]
        source_file.cpp(20) : see reference to class template instantiation 'std::unique_ptr<int,LockDeleter>' being compiled

注意提到std::_Unique_ptr_base&lt;_Ty,_Dx,false&gt;::_Unique_ptr_base(int *,_Dx);这表明删除器参数被错误地复制到内部基类中,应该移动它。

我能看到的唯一解决方法是使lock_ 可变,允许复制构造函数作为移动构造函数运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    • 2015-04-09
    • 1970-01-01
    相关资源
    最近更新 更多