Understanding move semantics

Hi everyone,

Just studying a bit of c++11. I’m trying to transfer the ownership of an object(Vehicle) from one vector to another one. Not sure if that makes much sense. I would like the object not to be created and destroyed, just moved.

Any other recommendations or criticisms (about the class structure, types used…) are very welcome.
This is just for the sake of study, the more I learn the better.
I’m trying to transfer ownership on buyAnyAvailableVehicle() (end of the file). Current code does not compile but using copies works fine.

Its a bit messy. I can isolate the move code if needed.

Thanks for reading!

in ofApp.h

#pragma once

#include "ofMain.h"

//------------------
enum VehicleType {
	car_type,
	bycicle_type,
	tricicle_type,
	unknown_type
};

//------------------
const static string toString(const VehicleType & type) {
	switch (type) {
	case car_type:
		return "car";
	case bycicle_type:
		return "bycicle";
	case tricicle_type:
		return "tricicle";
	case unknown_type:
		return "unknown";
	default:
		return "Undefined";
	}
}

//------------------
enum WheelPosition {
	front_pos,
	rear_pos,
	right_pos,
	left_pos,
	frontRight_pos,
	frontLeft_pos,
	rearRight_pos,
	rearLeft_pos
};
typedef std::set<WheelPosition> WheelPositionSet;

//------------------
const static string toString(const WheelPosition & position) {
	switch (position) {
	case front_pos:
		return "front";
	case rear_pos:
		return "rear";
	case right_pos:
		return "right";
	case left_pos:
		return "left";
	case frontRight_pos:
		return "frontRight";
	case frontLeft_pos:
		return "frontLeft";
	case rearRight_pos:
		return "rearRight";
	case rearLeft_pos:
		return "rearLeft";
	default:
		return "Undefined";
	}
}
//------------------
class Uncopyable {
protected:				// allow construction
	Uncopyable() {}		// and destruction of
	~Uncopyable() {}	// derived objects...
private:
	Uncopyable(const Uncopyable&);	// ...but prevent copying
	Uncopyable& operator= (const Uncopyable&);
};

//------------------
// let-s try make wheels uncopyable
class Wheel{// : private Uncopyable {
public:
	Wheel(int radius = 32) : radius(radius) {}
	int getRadius() { return this->radius; }
private:
	int radius;
};
// a vector<Wheel> will create copies.
// let's use a vector of pointers to wheels.
// they are unique_pointers coz only a vehicle will own them.
typedef vector<unique_ptr<Wheel>> Wheels; 

//------------------
class VehicleSpecs {
public:
	VehicleSpecs(const VehicleType & type) : type(type) {};
//	VehicleSpecs(const Wheels & other) : wheels(other) {};
	const VehicleType & getType() const { return this->type; }
	const WheelPositionSet & getWheelPositionSet() const { return this->wheelspecs; }
	void setType(VehicleType type) { this->type = type; }
	void addWheel(WheelPosition position) { this->wheelspecs.insert(position); }
	void removeWheel(WheelPosition position) { this->wheelspecs.erase(position); }
private:
	const VehicleSpecs(); // should we allow a default constructor? empty specs?
	WheelPositionSet	wheelspecs;
	VehicleType			type;
};

//------------------
// Abstract Base class (virtual funtions + some functions defined)
// not an Interface class, (only pure virtual funtions allowed)
class Vehicle : public ofNode{//, private Uncopyable{
public:
	// move constructor
	// this is important, since wheels cant be copied we need to move them
	// otherwise whats the point of a move operator..
	Vehicle(Vehicle && other) 
		: specs(std::move(other.specs)),
		 wheels(std::move(other.wheels)) 
	{
		std::cout << "move "<< std::endl;
	}

	// Base classes must have virtual destructor
	virtual ~Vehicle() {
//		std::cout << "destructor  " << std::endl;
	}	
	const VehicleType & getVehicleType() const { return this->specs.getType(); }
	Wheels & accessWheelSet() { return this->wheels; }
	VehicleSpecs & getSpecs() { return this->specs; }

	void printType() {
		cout << "This vehicle is a ";
		cout << toString(this->getSpecs().getType()) << endl;
	}

	void printInstalledWheels() {
		for (auto & wheelPos : this->getSpecs().getWheelPositionSet()) {
			cout << "This vehicle has a ";
			cout << toString(wheelPos);
			cout << " wheel" << endl;
		}
	}

protected:		// no one must implement vehicle, only inheritance allowed
	Vehicle(const VehicleType & type) : specs(type) {}
	//	Vehicle(const VehicleSpecs & incoming): specs(incoming) {}
	const VehicleSpecs & getSpecs() const { return this->specs; }	// getSpecs has to be available for all vehicles.
	virtual void moveForward() = 0 {	// The = 0 means this function is pure virtual
		this->move(1, 0, 0);
	};
	virtual void turnRight() = 0 {
		this->rollDeg(1);
	};
	virtual void turnLeft() = 0 {
		this->rollDeg(-1);
	};
	VehicleSpecs	specs;
	Wheels			wheels;
};
typedef shared_ptr<Vehicle> VehiclePtr;

//------------------
// this object will (or will not) have a threaded function, depending on specs.
// engine thread might run out of sync with GL thread.
class Car : public Vehicle {
public:
	Car() : Vehicle(car_type) {};

//	void startEngine();
//	void stopEngine();

	void moveForward() override {	// override
		this->move(1, 0, 0);		// do something
	};
	void turnRight() override {
		this->rollDeg(1);
	};
	void turnLeft() override {
		this->rollDeg(-1);
	};
};
typedef shared_ptr<Car> CarPtr;

//------------------
class Bicycle : public Vehicle {
public:
	Bicycle() : Vehicle(bycicle_type) {};

	void moveForward() override {	// override
		this->move(1, 0, 0);		// do something
	};
	void turnRight() override {
		this->rollDeg(1);
	};
	void turnLeft() override {
		this->rollDeg(-1);
	};
};

//------------------
class Tricicle : public Vehicle {
public:
	Tricicle() : Vehicle(tricicle_type) {};

	void moveForward() override {	// override
		this->move(1, 0, 0);		// do something
	};
	void turnRight() override {
		this->rollDeg(1);
	};
	void turnLeft() override {
		this->rollDeg(-1);
	};
};

//------------------
// why cant this be inside vehicle class??
static VehicleType getType(VehiclePtr vehicle) {
	if (dynamic_pointer_cast<Car>(vehicle)) {
		return car_type;
	}
	else if (dynamic_pointer_cast<Bicycle>(vehicle)) {
		return bycicle_type;
	}
	else if (dynamic_pointer_cast<Tricicle>(vehicle)) {
		return tricicle_type;
	}
	else {
		return unknown_type;
	}
}

//------------------
// a Factory Class
class Manufacturer {
public:
	const VehiclePtr makeVehicle(VehicleType type) {
		switch (type) {
		case car_type:
			VehiclesToSell.emplace_back(new Car);
			break;
		case bycicle_type:
			VehiclesToSell.emplace_back(new Bicycle);
			break;
		case tricicle_type:
			VehiclesToSell.emplace_back(new Tricicle);
			break;
		default:
			cout << "error!";
		}
		VehiclePtr currentVehicle = VehiclesToSell.back();
		installWeelset(*currentVehicle);
		return currentVehicle;
	}

	void installWeel(Vehicle& vehicle, WheelPosition position) {
		vehicle.getSpecs().addWheel(position);
		vehicle.accessWheelSet().emplace_back(new Wheel);
	}

	void installWeelset(Vehicle& vehicle) {
		switch (vehicle.getVehicleType()) {
		case car_type:
			installWeel(vehicle, frontRight_pos);
			installWeel(vehicle, frontLeft_pos);
			installWeel(vehicle, rearRight_pos);
			installWeel(vehicle, rearLeft_pos);
			break;
		case bycicle_type:
			installWeel(vehicle, front_pos);
			installWeel(vehicle, rear_pos);
			break;
		case tricicle_type:
			installWeel(vehicle, front_pos);
			installWeel(vehicle, rearRight_pos);
			installWeel(vehicle, rearLeft_pos);
			break;
		default:
			cout << "error!";
		}
	}

	Vehicle && sellSomeVehicle() {
		return std::move(*this->VehiclesToSell.back());
	}

	void printAmountOfVechiclesInStock() {
		cout << VehiclesToSell.size() << endl;
	}

private:
	vector<VehiclePtr> VehiclesToSell;

};

class User {
public:
	void buyAnyAvailableVehicle(Manufacturer& manufacturer) {
		vehicles.emplace_back(manufacturer.sellSomeVehicle());
	}
	void printAmountOfVechiclesOwned() {
		cout << vehicles.size() << endl;
	}
private:
	vector<VehiclePtr> vehicles;
};

class ofApp : public ofBaseApp {
public:
	void setup();
	void update();
	void draw();

	void keyPressed(int key);
	void keyReleased(int key);
	void mouseMoved(int x, int y);
	void mouseDragged(int x, int y, int button);
	void mousePressed(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void mouseEntered(int x, int y);
	void mouseExited(int x, int y);
	void windowResized(int w, int h);
	void dragEvent(ofDragInfo dragInfo);
	void gotMessage(ofMessage msg);

	Manufacturer	manufacturer;
	User			user;
};

in ofApp.cpp

void ofApp::setup(){

	VehiclePtr aVehicle;
	aVehicle = manufacturer.makeVehicle(car_type);
	aVehicle.get()->printType();
	aVehicle.get()->printInstalledWheels();
	manufacturer.printAmountOfVechiclesInStock();

	user.buyAnyAvailableVehicle(manufacturer);
	manufacturer.printAmountOfVechiclesInStock();
	user.printAmountOfVechiclesOwned();
}

First of all if you are using pointers move doesn’t make much sense anyway since you can just remove the pointer from the vector put it in the new vector and there would be no construction or destruction of the object anyway.

then you can’t move a pointer by dereferencing it like

return std::move(*this->VehiclesToSell.back());

because you would be turning the object froma heap object into a stack one but also even if you moved it the old vector still has a copy of the pointer since you are not removing it.

std::move doesn’t really move things, just tells the compiler that the passed value is an rvalue, it’s not going to be used after wards so it’s safe to move it but in c++ it’s the compiler that really decides if it needs to move or not.

in that method if you had a vector of objects, not pointers you could do:

auto car = std::move(vehiclesToShell.back());
vehiclesToShell.pop_back()
return car;

now there the car is really “moved” in the first line but the vector still has that position although it’s undefined, you still need to pop_back to remove the space in the vector.

there’s a more efficient way to move objects or even ranges of objects between vectors:

auto it = std::move(v1.begin(), v1.begin()+3, std::back_inserter(v2));
v1.erase(v1.begin(), it);

@arturo Thank you very much!
ok so I did totally misunderstood. std::move doesn’t move objects around.
What i vaguely understand now is:
std::move tells the compiler that a memory will not not be used anymore so it can be associated to a another pointer without the need of a copy?

Anyways…
So I kept the vector of pointers and just return a shared pointer like so:

const VehiclePtr sellSomeVehicle() {
	// create a new shared pointer
	auto vehicle = VehiclesToSell.back();
	// delete the vector enrty and its pointer
	VehiclesToSell.pop_back();
	// pass the pointer by value
	return vehicle;
}

About vector of pointers or vector of objects:
In this case (one vector for objects of different classes) I guess the only way to go is using a vector of pointers to base class, right? Otherwise I guess I will face the slicing problem. Or is there a better way to solve this without using pointers?

Thank you so much for the teachings! :bow:

yeah that’s it but you can’t magically avoid a copy in every case, depending on the structure of the object the only way to “move” it could be to copy it. for example an object that only contains variable with no pointers anywhere can only be copied but something like a vector which internally allocates memory in the heap can be moved for example by swapping the internal pointers to that memory.

and yes if you need polymorphism there’s no other way than having a vector of pointers

1 Like

Thank you so much!

My two cents… Bjarne Stroustrup: C++11 Style talking about the move operator

1 Like