【问题标题】:Automatic generated move operations and raw pointer members自动生成的移动操作和原始指针成员
【发布时间】:2014-03-10 17:33:22
【问题描述】:

考虑一个MemoryMappedFile 类,它具有以下数据成员:

class MemoryMappedFile
{
....
private:
    // RAII wrapper to some OS-specific raw handle (e.g. Win32 HANDLE),
    // representing the memory-mapped file.
    Handle m_handle;

    // Pointer to file begin (first file byte).
    BYTE* m_first;

    // Pointer to file end (one byte past last file byte).
    BYTE* m_last;
};

Handle 类是某些特定操作系统原始类 C 句柄的 RAII 包装器(例如,考虑 Win32 HANDLE)。它不可复制,但它是可移动的
相反,m_firstm_last 是映射到文件内容的内存区域内的原始指针。

我希望MemoryMappedFile可移动(但不可复制,就像Handle 类一样)。

如果不是原始指针,根据 C++11 的规则自动通过成员移动生成移动构造函数,该类将自动 可移动的。

不幸的是,原始指针迫使我编写自定义移动构造函数:

MemoryMappedFile::MemoryMappedFile(MemoryMappedFile&& other)
    : m_handle( std::move(other.m_handle) )
{
    // Move m_first
    m_first = other.m_first;
    other.m_first = nullptr;

    // Move m_last
    m_last = other.m_last;
    other.m_last = nullptr;
}

如果 C++ 标准库有某种形式的“笨拙但可移动”的指针就好了,它的开销为零,就像原始指针一样(观察 em> 非拥有指针),但定义了移动操作(移动构造函数和移动赋值),以便编译器可以在将这些指针作为数据成员的类中自动生成正确的移动操作。

在 C++ 标准库或 Boost 中是否有类似的东西?

或者有没有其他方法可以实现相同的目标(除了编写我自己的自定义 ObservingPointer 类、包装原始指针和定义移动操作)?

【问题讨论】:

  • std::unique_ptr 怎么样?它是零开销、不可复制、可移动的,如果您不想在销毁时使用 auto-delete,您可以给它一个自定义删除器。

标签: c++ pointers c++11 move


【解决方案1】:

只需将std::unique_ptr 与您自己的noop_deleter 一起使用。

【讨论】:

  • 感谢您的回答。但是,我不得不说这对我来说似乎有点“黑客”,因为我通常将std::unique_ptrunique ownership 相关联,所以它很友善unique_ptr 的行为就像一个 observing 指针(并且有一个 NOOP 删除器)...
  • @Mr.C64:所以给它起一个不暗示所有权的名称,例如template <typename T> using movable_ptr = std::unique_ptr<T, noop_deleter>;
  • non_owning_ptr 是一个更好的名字,这使得它真的明确没有所有权:)(这是N3840中使用的名字)
  • ownership_agnostic_pointer_that_zeroes_itself_on_move 更具描述性,但我认为smart_pointer_thats_just_like_a_raw_pointer_except_sets_itself_to_null_when_moved_from 更能捕捉程序员的意图。
【解决方案2】:

我经常需要一个“智能”指针,它既可复制又可移动,但具有明确定义的移动状态,所以我写了tidy_ptr,这是一个“哑”智能指针,除了零本身没有什么特别的在移动。该类型是可复制的,因此要为您的类获得所需的语义,您仍然需要将复制操作定义为已删除(或仅使用 std::unique_ptr 和无操作删除器)。

我试图让标准委员会相信observer_ptr, "the world's dumbest smart pointer", 应该有这种行为,但大家一致认为它的行为应该像一个内置指针(构造函数中的零初始化除外)。我仍然认为它应该在移动中归零。那篇论文显示了一个non_owning_ptr,它是unique_ptr 的别名,带有一个无操作删除器,可以满足您的要求。

【讨论】:

  • 我相信observer_ptr 应该在移动后清零,就像stringvector 等发生的情况一样。使observer_ptr 的移动行为与复制行为相同不会对我没有意义。感谢您分享您的tidy_ptr 课程。
  • 但不能保证stringvector在移动后为空。它们处于有效但未指定状态。某些类型,例如unique_ptrshared_ptrunique_lockfuture(和我的tidy_ptr)提供了在移动后处于已知状态的更强保证,容器不保证这一点。
  • 在阅读您的评论之前,我认为移动后stringvector 将处于安全的空状态(例如vector.empty() == true)。
  • 我相信在特定包装器中获得移动后归零操作比observer_ptr 更好。我实现了一个通用的NullablePointer 包装器here,名为nptr。请注意,它确实 NOT 要求底层指针是内置指针类型。然后使用template<T> using tidy_ptr = nptr<observer_ptr<T>>; 是微不足道的。
  • @FrankHB,是的,我有一个更通用的tidy_ptr 版本,它适用于任何默认可构造的东西,而不仅仅是原始指针。我不确定那个代码现在在哪里。
猜你喜欢
  • 2021-11-07
  • 2015-10-01
  • 2015-10-28
  • 2014-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
相关资源
最近更新 更多