c++singletonc++20unique-ptrstdmap

Singleton class with std map of unique_ptr is throwing error on compilation


The following code is failing to compile and i am not able to figure out why!


/* --------------------------------------------------------------- */
/* Includes */
#include <iostream>
#include <map>
#include <memory>
/* --------------------------------------------------------------- */

/* Forward delcarations */
class Person;
/* --------------------------------------------------------------- */

/* Using definitions */
using PersonUptr = std::unique_ptr<Person>;
/* --------------------------------------------------------------- */

/** @brief Person class to hold a record */
class Person {
public:
    /** @brief Explicit Ctor */
    explicit Person(const std::string& name)
                : m_name(name) {}

    /** @brief Person must stay unique */
    Person(const Person&) = delete;
    Person(Person&&) = delete;
    Person& operator=(const Person&) = delete;
    Person& operator=(Person&&) = delete;

    /** @virtual Dtor */
    virtual ~Person() =  default;

    /** @brief Return name/key */
    const std::string& getName(void) const {
        return m_name;
    }
private:
    /** @brief Class member variables */
    std::string m_name = "";
};

/** @brief Singleton container for storing Person instances */
class PersonContainer {
public:
    /** @brief Virtual Dtor */
    virtual ~PersonContainer() = default;

    /** @brief Delete copy/move */
    PersonContainer(const Person&) = delete;
    PersonContainer(Person&&) = delete;
    PersonContainer& operator=(const Person&) = delete;
    PersonContainer& operator=(Person&&) = delete;

    /** @brief Singleton instance */
    static PersonContainer& getInstance(void) {
        static PersonContainer container;
        return container;
    }

    /** @brief add Person unique ptr to map */
    void addPerson(PersonUptr&& person) {
        /** FIXME Check duplicate add */
        m_person_map.insert({person->getName(), std::move(person)});
    }
private:
    /** @brief Singleton private Ctor */
    PersonContainer() = default;

    /** @brief Class members */
    std::map<std::string, PersonUptr> m_person_map;
};

/** @brief Driver code */
int main(void) {

    /** Get container handle */
    auto handle = PersonContainer::getInstance();

    return 0;
}

Error:

In file included from personal-record.cc:4:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream:43:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/ios:222:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__locale:15:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/shared_ptr.h:22:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/allocation_guard.h:15:
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/allocator_traits.h:304:9: error: no matching function for call to 'construct_at'
        _VSTD::construct_at(__p, _VSTD::forward<_Args>(__args)...);
        ^~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__config:897:17: note: expanded from macro '_VSTD'
\#  define _VSTD std
                ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__tree:2144:20: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<std::__tree_node<std::__value_type<std::string, std::unique_ptr<Person>>, void *>>>::construct<std::pair<const std::string, std::unique_ptr<Person>>, const std::pair<const std::string, std::unique_ptr<Person>> &, void, void>' requested here
    __node_traits::construct(__na, _NodeTypes::__get_ptr(__h->__value_), _VSTD::forward<_Args>(__args)...);
                   ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__tree:2127:29: note: in instantiation of function template specialization 'std::__tree<std::__value_type<std::string, std::unique_ptr<Person>>, std::__map_value_compare<std::string, std::__value_type<std::string, std::unique_ptr<Person>>, std::less<std::string>>, std::allocator<std::__value_type<std::string, std::unique_ptr<Person>>>>::__construct_node<const std::pair<const std::string, std::unique_ptr<Person>> &>' requested here
        __node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
                            ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__tree:1274:16: note: in instantiation of function template specialization 'std::__tree<std::__value_type<std::string, std::unique_ptr<Person>>, std::__map_value_compare<std::string, std::__value_type<std::string, std::unique_ptr<Person>>, std::less<std::string>>, std::allocator<std::__value_type<std::string, std::unique_ptr<Person>>>>::__emplace_hint_unique_key_args<std::string, const std::pair<const std::string, std::unique_ptr<Person>> &>' requested here
        return __emplace_hint_unique_key_args(__p, _NodeTypes::__get_key(__v), __v).first;
               ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/map:1318:29: note: in instantiation of member function 'std::__tree<std::__value_type<std::string, std::unique_ptr<Person>>, std::__map_value_compare<std::string, std::__value_type<std::string, std::unique_ptr<Person>>, std::less<std::string>>, std::allocator<std::__value_type<std::string, std::unique_ptr<Person>>>>::__insert_unique' requested here
            {return __tree_.__insert_unique(__p.__i_, __v);}
                            ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/map:1339:17: note: in instantiation of member function 'std::map<std::string, std::unique_ptr<Person>>::insert' requested here
                insert(__e.__i_, *__f);
                ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/map:1147:13: note: in instantiation of function template specialization 'std::map<std::string, std::unique_ptr<Person>>::insert<std::__map_const_iterator<std::__tree_const_iterator<std::__value_type<std::string, std::unique_ptr<Person>>, std::__tree_node<std::__value_type<std::string, std::unique_ptr<Person>>, void *> *, long>>>' requested here
            insert(__m.begin(), __m.end());
            ^
personal-record.cc:43:7: note: in instantiation of member function 'std::map<std::string, std::unique_ptr<Person>>::map' requested here
class PersonContainer {
      ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/__memory/construct_at.h:39:38: note: candidate template ignored: substitution failure [with _Tp = std::pair<const std::string, std::unique_ptr<Person>>, _Args = <const std::pair<const std::string, std::unique_ptr<Person>> &>]: call to implicitly-deleted copy constructor of 'std::pair<const std::string, std::unique_ptr<Person>>'
_LIBCPP_HIDE_FROM_ABI constexpr _Tp* construct_at(_Tp* __location, _Args&&... __args) {
                                     ^
1 error generated.

Solution

  • In this line:

    auto handle = PersonContainer::getInstance();
    

    You attempt to create a copy of the singleton because auto does not deduce a reference. You get an error because the std::map member in the singleton (m_person_map) contains unique_ptrs and is therefore non-copyable.

    In order to get the reference you need to change it to:

    //---v-----------------------------------------
    auto & handle = PersonContainer::getInstance();