#include <string>
#include <memory>
#include <array>

typedef std::shared_ptr<std::string> pString;

struct A{
protected:
        static constexpr size_t array_size = 2;
        std::array<pString, array_size> m_names;
        static void CheckRange(size_t);
public:
        void SetName(size_t id, const std::string& name);
        void SetName(size_t id, const pString& name);

        const std::string& GetName(size_t id) const;
        const pString& GetNamePtr(size_t id) const;
};

#include <exception>

void A::SetName(size_t id, const pString &name){
        CheckRange(id);
        m_names[id] = name;
}

void A::SetName(size_t id, const std::string& name){
        CheckRange(id);
        m_names[id] = std::make_shared<std::string>(name);
}

const std::string& A::GetName(size_t id) const{
        CheckRange(id);
        if (!m_names[id])
                throw std::logic_error("Pointer is not initialized");
        return *(m_names[id].get());
}

const pString& A::GetNamePtr(size_t id) const {
        CheckRange(id);
        return m_names[id];
}

void A::CheckRange(size_t id){
        if (!(id < array_size))
                throw std::logic_error("ID should be < " + std::to_string(array_size));
}

#include <iostream>
bool TestSetString(){
        A a;
        std::string s1("Test 1"), s2("Test 2");
        a.SetName(0, s1);
        a.SetName(1, s2);

        return a.GetName(0) == s1 && a.GetName(1) == s2;
}

bool TestSetPointer(){
        A a;
        pString s1 = std::make_shared<std::string>("Test 1");
        pString s2 = std::make_shared<std::string>("Test 2");

        a.SetName(0, s1);
        a.SetName(1, s2);

        return a.GetNamePtr(0) == s1 && a.GetNamePtr(1) == s2;
}

bool TestAccessNotInit(){
        A a;
        try {
                a.GetName(0);
                return false;
        } catch (const std::logic_error& e){
                std::cerr << "\tCatched exception: " << e.what() << std::endl;
                return true;
        }
}

bool TestAccessRange(){
        A a;
        try {
                a.GetName(10);
                return false;
        } catch (const std::logic_error& e){
                std::cerr << "\tCatched exception: " << e.what() << std::endl;
                return true;
        }
}

int main(){
        std::cout << "TestSetString: " << std::boolalpha << TestSetString() << std::endl;
        std::cout << "TestSetPointer: " << std::boolalpha << TestSetPointer() << std::endl;
        std::cout << "TestAccessNotInit: " << std::boolalpha << TestAccessNotInit() << std::endl;
        std::cout << "TestAccessRange: " << std::boolalpha << TestAccessRange() << std::endl;
        return 0;
}