简体   繁体   中英

A uniform copy and move constructor of a template class

Let assume I have a DataProcessor template class that holds a smart pointer to the processed data that has operator * and 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;
}

How to implement the constructor to make DataProcessor work with both std::unique_ptr (the constructor should accept it by && and move it) and std::shared_ptr (the constructor should accept it by & and copy it)? Is it possible to have some kind of a uniform constructor?

Actually I have a class that holds a smart Win32 handle UniqueHandle and SharedHandle that have the similar semantics like std::unique_ptr and std::shared_ptr. So it is a general question on how to implement a scenario like this.

Your choices are essentially these:

  1. Take the parameter by value:

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

    If DataPointer is move-only, then the user will have to call it through std::move , which will move-construct p , which is then used to move-construct pData . If it is copyable, then p will be copy/move constructed based on how the user passes the value. From there, it will move construct pData .

    Note that this version adds an additional move operation.

  2. Take the parameter by rvalue reference, always:

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

    In this case, if DataPointer is not move-only, and the user wants to pass an lvalue, the user must explicitly copy the value into a temporary used to initialize p . That would look something like this:

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

    Where sp is an existing shared_ptr that you want to copy from. This does only one move when given an object to move from, but does a copy+move when copying.

  3. Write two functions, employing SFINAE to remove the copying version if DataPointer is non-copyable. This version has the advantage of doing no additional moves:

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) {}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM