Why does std::move cause SEGFAULT in this case?

741 views Asked by At

Why does std::move() cause a SEGFAULT in this case?

#include <iostream>

struct Message {
    std::string message;
};

Message * Message_Init(std::string message) {
    Message * m = (Message*)calloc(1, sizeof(Message));
    m->message = std::move(message);
    return m;
}

int main() {
    auto m = Message_Init("Hello");
    return 0;
}

P.S. Please don't ask why Message is not constructed in a usual C++ manner.

2

There are 2 answers

1
Frodyne On BEST ANSWER

If you really want to do something like this, then you can use placement new. This allows you to construct an object in a chunk of memory that is already allocated.

Several of the standard containers use placement new to manage their internal buffers of objects.

However, it does add complications with the destructors of the objects you place. Read here for more info: What uses are there for "placement new"?

#include <iostream>
#include <memory>

struct Message {
    std::string message;
};

Message * Message_Init(std::string message) {
    void * buf = calloc(1, sizeof(Message));
    Message * m = new (buf) Message(); // placement new
    m->message = std::move(message);
    return m;
}

int main() {
    auto m = Message_Init("Hello");
    m->~Message();  // Call the destructor explicitly
    free(m);        // Free the memory
    return 0;
}

As @selbie suggested, I have added an explicit call to the destructor of Message, and also a call to free to deallocate the memory. I believe that the call to free should actually have pointed to the buffer originally returned by calloc since there could be a difference (so free(buf) in this case, but that pointer is not accessible here).

For example if you allocate a buffer for several objects, then calling free on the pointer to the second object would not be correct.

0
Jakob Stark On

There seems to be a misunderstanding about how assignment in C++ works. If the compiler encounters a assigment operation with class types involved, it looks for a special function called the copy/move assignment operator. This special function is then called to perform the copy/move. Generally speaking the left hand side and the right hand side have to be initialized objects for this to work correctly.

There is a special case in which it is allowed to use uninitialized objects. That is the case where the class of the objects that are to be copied/moved has a trivial copy/move assigment operator. However most of the standard library classes are not trivially copiable/moveable and in particular std::string is not.

By the way it does not matter that you called std::move. Even without it, it would be undefined behaviour. You have to construct the target object before assigning to it. You can do that by either allocating and constructing the object in one shot by calling the normal new operator, or by allocating the space and use placement new to construct the object in that space.