首页 > 解决方案 > 模板类的统一复制和移动构造函数

问题描述

假设我有一个 DataProcessor 模板类,它包含一个智能指针,指向具有 operator * 和 operator -> 的已处理数据:

template <class DataPointer>
class DataProcessor
{
    public:

        //it is not clear how to implement the constructor
        //DataProcessor(DataPointer p) : pData(p) {};

        void Process() { /* do something with *pData */ };

    private:

        DataPointer pData;
}

如何实现构造函数以使 DataProcessor 与 std::unique_ptr (构造函数应该通过 && 接受它并移动它)和 std::shared_ptr (构造函数应该通过 & 接受它并复制它)一起工作?是否有可能有某种统一的构造函数?

实际上,我有一个类拥有一个智能 Win32 句柄 UniqueHandle 和 SharedHandle,它们具有类似的语义,如 std::unique_ptr 和 std::shared_ptr。因此,如何实现这样的场景是一个普遍的问题。

标签: c++c++17

解决方案


您的选择基本上是这些:

  1. 按值取参数:

    DataProcessor(DataPointer p) : pData(std::move(p)) {}
    

    如果DataPointer是 move-only,那么用户必须通过 调用它std::move,这将p是 move-construct ,然后用于 move-construct pData。如果它是可复制的,那么p将根据用户传递值的方式来构造复制/移动。从那里,它将移动构造pData

    请注意,此版本添加了额外的移动操作。

  2. 通过右值引用获取参数,始终:

    DataProcessor(DataPointer &&p) : pData(std::move(p)) {}
    

    在这种情况下,如果DataPointer不是仅移动,并且用户想要传递一个左值,则用户必须显式将该值复制到用于初始化的临时值中p。看起来像这样:

    DataProcessor<shared_ptr<T>> dp(shared_ptr{sp});
    

    您要从中复制sp的现有对象在哪里。shared_ptr当给定一个要移动的对象时,这只会移动一次,但在复制时会进行复制+移动。

  3. DataPointer编写两个函数,如果不可复制,则使用 SFINAE 删除复制版本。这个版本的优点是不需要做额外的动作:

DataProcessor(DataPointer &&p) : pData(std::move(p)) {}
template<typename T = DataPointer>
DataProcessor(std::enable_if_t<std::is_copy_constructible_v<T>, const T&> p)
        : pData(p) {}

推荐阅读