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

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-12 09:32:50

问题


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 cases for a class which can be copied but not moved. Technically, this is possible:

struct S
{
    S() { }
    S(S const& s) { }
    S(S&&) = delete;
};

S foo()
{
    S s1;
    S s2(s1); // OK (copyable)
    return s1; // ERROR! (non-movable)
}

Although S has a copy constructor, it obviously does not model the CopyConstructible concept, because that is in turn a refinement of the MoveConstructible concept, which requires the presence of a (non-deleted) move constructor (see § 17.6.3.1/2, Table 21).

Is there any use case for a type like S above, which is copyable but not CopyConstructible and non-movable? If not, why is it not forbidden to declare a copy constructor and a deleted move constructor in the same class?


回答1:


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 :-)




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/14323093/are-there-any-use-cases-for-a-class-which-is-copyable-but-not-movable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!