I have created a template class Gallery which is intended to be used as a container for objects. I used a private member of typestd::map to maintain the collection alongside other functions. This class was not intended to be used on production code but rather to help me practice design and coding skills. I tried to incorporate templates, Inheritance, Polymorphism, smart pointers, mutators and accessors, a few data structures, operator overloading, the big five, and exceptions. The driver Program doesn't test everything since I tried to save on space.
Be gentle, I am only a newbie.
How can this class be designed and written better?
/**
The Gallery Class can be used to hold a map of objects that has some sort of unique identifier.
The object should implement the functions:
    const Identifier& getName();
    std::unique_ptr<LabeledObject> clone() const;
    overload: <, ==, >, and <<
*/
#ifndef GALLERY_H
#define GALLERY_H
#include <map>
#include <unordered_set>
#include <string>
#include <iostream>
#include "shape.h"
// Gallery of Objects
template<typename LabeledObject, typename Identifier> class Gallery{
public:
    /// constructors
    Gallery() = default;
    Gallery(const Gallery& galleryB);
    Gallery(Gallery&& galleryB);
    /// getters
    LabeledObject& objectCheckOut(const Identifier& name);
    const LabeledObject& viewObject(const Identifier& name) const;
    /// overloaded operators
    Gallery& operator=(const Gallery& galleryB);
    Gallery& operator=(Gallery&& galleryB);
    bool operator<(const Gallery& galleryB) const;  // compare sizes
    bool operator>(const Gallery& galleryB) const;  // compare sizes
    bool operator==(const Gallery& galleryB) const; // compare owned objects
    Gallery& operator+(const Gallery& galleryB);    // append galleryB's objects
    Gallery& operator-(const Gallery& galleryB);    // erase objects that resemble galleryB's objects
    template<typename T, typename U> friend std::ostream& operator<<(std::ostream& out, Gallery<T,U> galleryB);
    /// other utility functions
    void removeObject(const Identifier& name);
    bool isCheckedOut(const Identifier& name) const;
    bool objectExists(const Identifier& name) const;
    void objectCheckIn(const Identifier& name);
    void addObject(const LabeledObject& obj);
private:
    // boolean to show if checked out
    std::map<Identifier, std::pair<std::unique_ptr<LabeledObject>, bool>> objectsMap;
    /// private exception classes
    class GalleryException{
        private: std::string description;
        public:
            GalleryException(const std::string& description = "Exception Occurred in Gallery Class")
                : description(description){}
            const std::string& getException() const;
    };
    class ObjectCheckedOut : public GalleryException{
        public:
            ObjectCheckedOut(const std::string& description = "Object Checked Out Exception Occurred in Gallery Class")
                : GalleryException(description){}
    };
    class ObjectNotInGallery: public GalleryException{
        public:
            ObjectNotInGallery(const std::string& description = "Object Not In Gallery Exception Occurred in Gallery Class")
                : GalleryException(description){}
    };
    class ObjectNotCheckedOut: public GalleryException{
        public:
            ObjectNotCheckedOut(const std::string& description = "Object Not Checked Out Exception Occurred in Gallery Class")
                : GalleryException(description){}
    };
    /// private functions
    void appendToGallery(const Gallery& galleryB);
    static void handleException(GalleryException&&);
};
#endif
/************************************************************************************************
*************************************************************************************************
*   FUNCTION DEFINITIONS                                                                        *
*   -----------------------------                                                               *
*       Note that our class is a template hence functions are best defined within the.h file    *
*                                                                                               *
*************************************************************************************************
*************************************************************************************************/
/// constructors
    /// copy constructor
    template<typename LabeledObject, typename Identifier>
    Gallery<LabeledObject,Identifier>::Gallery(const Gallery& galleryB){
        appendToGallery(galleryB);
    }
    /// move constructor
    template<typename LabeledObject, typename Identifier>
    Gallery<LabeledObject, Identifier>::Gallery(Gallery&& galleryB)
    :objectsMap(std::move(galleryB.objectsMap)){}
/// getters
    /// viewObject
    template<typename LabeledObject, typename Identifier>
    const LabeledObject& Gallery<LabeledObject, Identifier>::viewObject(const Identifier& key) const{
        if(!objectExists(key))
            handleException(ObjectNotInGallery());
        else if(isCheckedOut(key))
            handleException(ObjectCheckedOut());
        // returning reference of unique_ptr from : [Identifier, {unique_ptr, true/false}]
        return *((objectsMap.find(key) -> second).first);
    }
    /// objectCheckOut
    template<typename LabeledObject, typename Identifier>
    LabeledObject& Gallery<LabeledObject, Identifier>::objectCheckOut(const Identifier& key){
        if(!objectExists(key))
            handleException(ObjectNotInGallery());
        else if(isCheckedOut(key))
            handleException(ObjectCheckedOut());
        // change false to true in mapElement : [Identifier, {unique_ptr, false}]
        objectsMap[key].second = true;     // label as checked out
        return *(objectsMap[key].first);
    }
/// utility functions
    /// addObject
    template<typename LabeledObject, typename Identifier>
    void Gallery<LabeledObject, Identifier>::addObject(const LabeledObject& obj){
        // new Element is in the form [Identifier, {unique_ptr, true/false}]
        objectsMap[obj.getName()] = std::make_pair(obj.clone(), false);
    }
    /// objectCheckIn
    template<typename LabeledObject, typename Identifier>
    void Gallery<LabeledObject, Identifier>::objectCheckIn(const Identifier& key){
        objectsMap[key].second = false;
    }
    /// removeObject
    template<typename LabeledObject, typename Identifier>
    void Gallery<LabeledObject, Identifier>::removeObject(const Identifier& key) {
        if(objectExists(key))
            objectsMap.erase(key);
    }
    /// isCheckedOut
    template<typename LabeledObject, typename Identifier>
    bool Gallery<LabeledObject, Identifier>::isCheckedOut(const Identifier& key) const{
        //find() returns true/false form : [Identifier, {unique_ptr, true/false}]
        return objectExists(key) && (objectsMap.find(key)-> second).second;
    }
    /// objectExists
    template<typename LabeledObject, typename Identifier>
    bool Gallery<LabeledObject, Identifier>::objectExists(const Identifier& key) const{
        return objectsMap.find(key) != objectsMap.end();
    }
/// overloaded operators
    /// assignment operator
    template<typename LabeledObject, typename Identifier>
    Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator=(const Gallery& galleryB){
        if(objectsMap != galleryB.objectsMap){
            objectsMap.clear();
            appendToGallery(galleryB);
        }
        return *this;
    }
    /// move assignment
    template<typename LabeledObject, typename Identifier>
    Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator=(Gallery&& galleryB){
        objectsMap = std::move(galleryB.objectsMap);
        return *this;
    }
    /// == operator
    template<typename LabeledObject, typename Identifier>
    bool Gallery<LabeledObject, Identifier>::operator==(const Gallery& galleryB) const{
        return objectsMap.size() == galleryB.objectsMap.size();
    }
    /// < operator
    template<typename LabeledObject, typename Identifier>
    bool Gallery<LabeledObject, Identifier>::operator<(const Gallery& galleryB) const{
        return objectsMap.size() < galleryB.objectsMap.size();
    }
    /// > operator
    template<typename LabeledObject, typename Identifier>
    bool Gallery<LabeledObject, Identifier>::operator>(const Gallery& galleryB) const{
        return objectsMap.size() > galleryB.objectsMap.size();
    }
    /// + operator
    template<typename LabeledObject, typename Identifier>
    Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator+(const Gallery& galleryB){
        appendToGallery(galleryB);
        return *this;
    }
    /// - operator
    template<typename LabeledObject, typename Identifier>
    Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator-(const Gallery& galleryB){
        for(auto it = galleryB.objectsMap.begin(); it != galleryB.objectsMap.end(); ++it)
            removeObject(it -> first);
        return *this;
    }
    /// << operator
    template<typename LabeledObject, typename Identifier>
    std::ostream& operator<<(std::ostream& out, Gallery<LabeledObject, Identifier> galleryB){
        // it points to an element in the form : [Identifier, {unique_ptr, true/false}]
        for(auto it = galleryB.objectsMap.begin(); it != galleryB.objectsMap.end(); ++it){
            std::string status = (it -> second).second ? "checked out \n" :"available \n";
            out << "This Object is " << status;
            out << ((it ->second).first) -> clone();
        }
        return out;
    }
/// private member functions
    /// appendToGallery
    template<typename LabeledObject, typename Identifier>
    void Gallery<LabeledObject, Identifier>::appendToGallery(const Gallery<LabeledObject, Identifier>& galleryB){
        // s is in the form : [Identifier, {unique_ptr, true/false}]
        for(auto s = galleryB.objectsMap.begin(); s != galleryB.objectsMap.end(); ++s)
            addObject(*((s -> second).first));
    }
    /// handleException
    template<typename LabeledObject, typename Identifier>
    void Gallery<LabeledObject, Identifier>::handleException(GalleryException&& exp){
        try{
            throw exp;
        }catch(GalleryException){
            std::cout << exp.getException();
        }
    }
/// functions of private member classes
    /// getException
    template<typename LabeledObject, typename Identifier>
    const std::string& Gallery<LabeledObject, Identifier>::GalleryException::getException() const{
        return description;
    }
/******************************************
/* DRIVER PROGRAM
/*****************************************/
#include <iostream>
#include <vector>
#include "location.h"
#include "shape.h"
#include "circle.h"
#include "rectangle.h"
#include "gallery.h"
using namespace std;
int main() {
    Location a(2,4, "A");
    Location c(5,3, "C");
    vector<shared_ptr<Shape>> shapes {make_shared<Shape>(c, "Tree", 35), make_shared<Circle>(c, "Circle C", 4), make_shared<Rectangle>(c, 6, 7, "Rec1")};
    Gallery<Shape, std::string> g, h;
    for(auto s: shapes)
        g.addObject(*c);
    h = g;
    cout << g << h;
 
    return 0;
}
```


