graham.reeds graham.reeds - 2 months ago 6x
C++ Question

Default move constructor

I have a database class and the explicit constructor tries to connect to the database based on flags passed in, if it fails then it throws. This is not desired (the database may not of been created by another app) so I added a blank constructor and default move constructors. In a utility class I wait until the database is created and move a new one in.

In unit tests I see that the

returns false before I move and true after the move. However if I call a function that uses the database I get a
library routine called out of sequence
error. This would suggest I haven't opened the database or a malformed select statement but the constructors and destructor are called in the correct order and I have unit tests for the database itself where it creates the database, populates it and the select statement works.

So my question: Is the default move actually moving it or not? If not what do I need to do to obtain the expected behaviour?

Sample code:

class database
database() : connected_(false), database_(nullptr) { }
database(/* params */) : connected_(false), database_(nullptr) {
/* attempt connection, throw on fail */
connected_ = true;
database(database& other) = default;
database(database&& other) = default;
database& operator=(database&& other) = default;
~database() { /* clean up */ }
operator bool() const { return connected_; }

bool connected_;
sqlite3* database_;

class database_utils
database_utils() : db_() { }
void connect() {
db_ = std::move(database(/*params*/));
bool connected() { return db_; }
void example_select(/* params */) {
/* use db_ */
database db_;


Default move constructor do move everything.

However database_ is pointer. Pointer move is actually copy.

Then in the destructor, database_ will be deleted.

Because both "old" and "new" object's database_ points to same memory location, "new" object will be in non stable state, since database_ will point into hyperspace.

If you can change database_ to be smart pointer, the default move constructor will work just well.

Alternatively, make your own move constructor that move everything and set database_ to nullptr and connected_ to false.