Are there any use cases for a class which is copyable but not movable?

前端 未结 5 1426
名媛妹妹
名媛妹妹 2020-12-18 20:22

After reading this recent question by @Mehrdad on which classes should be made non-movable and therefore non-copyable, I starting wondering if there are use

相关标签:
5条回答
  • 2020-12-18 20:46

    I currently know of no use cases for a deleted move constructor/assignment. If done carelessly it will needlessly prevent a type from being returned from a factory function or put into a std::vector.

    However deleted move members are legal nevertheless just in case someone might find a use for them. As an analogy, I knew of no use for const&& for years. People asked me if we shouldn't just outlaw it. But eventually a few use cases did show up after we got sufficient experience with the feature. The same might also happen with deleted move members, but to the best of my knowledge hasn't yet.

    0 讨论(0)
  • 2020-12-18 20:48

    I don't think there can be any reasonable class which would prevent move, yet allow copy. It is clear from the very same topic, that move is just an efficient way of copy when you don't need the original object anymore.

    0 讨论(0)
  • 2020-12-18 21:00

    Suppose you have a class that is no cheaper to move than it is to copy (perhaps it contains a std::array of a POD type).

    Functionally, you "should" make it MoveConstructible so that S x = std::move(y); behaves like S x = y;, and that's why CopyConstructible is a sub-concept of MoveConstructible. Usually if you declare no constructors at all, this "just works".

    In practice, I suppose that you might want to temporarily disable the move constructor in order to detect whether there is any code in your program that appears more efficient than it really is, by moving instances of S. To me it seems excessive to forbid that. It's not the standard's job to enforce good interface design in completed code :-)

    0 讨论(0)
  • 2020-12-18 21:00

    I was looking at this very issue today, because we've ported some code from VS2005 into VS2010 and started seeing memory corruption. It turned out to be because an optimization (to avoid copying when doing a map lookup) didn't translate into C++11 with move semantics.

    class CDeepCopy
    {
    protected:
        char* m_pStr;
        size_t m_length;
    
        void clone( size_t length, const char* pStr )
        {
            m_length = length;
            m_pStr = new char [m_length+1];
            for ( size_t i = 0; i < length; ++i )
            {
                m_pStr[i] = pStr[i];
            }
            m_pStr[length] = '\0';
        }
    
    public:
        CDeepCopy() : m_pStr( nullptr ), m_length( 0 )
        {
        }
    
        CDeepCopy( const std::string& str )
        {
            clone( str.length(), str.c_str() );
        }
    
        CDeepCopy( const CDeepCopy& rhs )
        {
            clone( rhs.m_length, rhs.m_pStr );
        }
    
        CDeepCopy& operator=( const CDeepCopy& rhs )
        {
            if (this == &rhs)
                return *this;
    
            clone( rhs.m_length, rhs.m_pStr );
            return *this;
        }
    
        bool operator<( const CDeepCopy& rhs ) const
        {
            if (m_length < rhs.m_length)
                return true;
            else if (rhs.m_length < m_length)
                return false;
    
            return strcmp( m_pStr, rhs.m_pStr ) < 0;
        }
    
        virtual ~CDeepCopy()
        {
            delete [] m_pStr;
        }
    };
    
    class CShallowCopy : public CDeepCopy
    {
    public:
    
        CShallowCopy( const std::string& str ) : CDeepCopy()
        {
            m_pStr = const_cast<char*>(str.c_str());
            m_length = str.length();
        }
    
        ~CShallowCopy()
        {
            m_pStr = nullptr;
        }
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::map<CDeepCopy, int> entries;
        std::string hello( "Hello" );
    
        CDeepCopy key( hello );
        entries[key] = 1;
    
        // Named variable - ok
        CShallowCopy key2( hello );
        entries[key2] = 2;
    
        // Unnamed variable - Oops, calls CDeepCopy( CDeepCopy&& )
        entries[ CShallowCopy( hello ) ] = 3;
    
        return 0;
    }
    

    The context was that we wanted to avoid unnecessary heap allocations in the event that the map key already existed - hence, the CShallowCopy class was used to do the initial lookup, then it would be copied if this was an insertion. The problem is that this approach doesn't work with move semantics.

    0 讨论(0)
  • 2020-12-18 21:02

    It depends on how you define the semantics of the move operation for your type. If move merely means optimized copy through resource-stealing then the answer is probably no. But the answer might be yes if move means "relocate" in the sense used by a moving garbage collector or some other custom memory management scheme.

    Consider a real-world example of a house located on a particular street address. One can define a copy of that house to be another house built using the exact same blueprints, but located on another address. Under these terms, we can go on saying that a house cannot be moved, because there might be people referring to it by its address. Translating to technical terms, the move operation might not be possible for structures that have inbound pointers.

    I can imagine a bit twisted implementation of a signals/slots library that allows its signals objects to be copied, but doesn't allow them to be moved.

    disclaimer: some C++ purists will point out that STL (and thus the standard) defines what a move operation is and it's not in line with what I described here so I won't argue with that.

    0 讨论(0)
提交回复
热议问题