8

I'm currently write a weak reference resource manager as below, and the compiler complained Manager has a private constructor.

My question is that: why can't I access the private member function in the static function?

#ifndef TENSOR_MANAGER_H
#define TENSOR_MANAGER_H

#include <memory>
#include <functional>
#include <map>
#include <iostream>

template<typename key_t, typename model_t>
class Manager : public std::enable_shared_from_this<Manager<key_t, model_t> > {
public:
    using self_t = Manager<key_t, model_t>;

public:
    static auto Create() {
        return std::make_shared<self_t>();
    }

public:

    std::shared_ptr<model_t> GetOrAdd(const key_t &&key, const char *data, size_t size) {
        auto pos = m_resources.find(key);
        std::shared_ptr<model_t> tmp;
        if (pos != m_resources.end()) {
            tmp = pos->second.lock();
        }
        if (!tmp) {
            model_t *p = new model_t();

            auto deletor = std::bind(&self_t::releaseItem,
                                     std::weak_ptr<self_t>(this->shared_from_this()),
                                     key,
                                     std::placeholders::_1);

            tmp = std::shared_ptr<model_t>(p, deletor);

            m_resources[key] = std::weak_ptr<model_t>(tmp);
        }
        return tmp;
    }

public:
    void Print() {
        std::cout << "Content: ";
        for (const auto &item : m_resources) {
            std::cout << item.first << " ";
        }
        std::cout << std::endl;
    }

private:
    static void releaseItem(std::weak_ptr<self_t> self, const key_t &key, model_t *p) {
        std::shared_ptr<Manager<key_t, model_t>> mgr = self.lock();
        if (mgr) {
            mgr->m_resources.erase(key);
        }
        delete p;
    }

private:
    std::map<key_t, std::weak_ptr<model_t> > m_resources;

    Manager() = default;
};

#endif //TENSOR_MANAGER_H

Now the compiler gives the errors:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/memory:2043:66: error: field of type 'Manager<std::__1::basic_string<char>, int>' has private default constructor
_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {}
1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
Jimmy Su
  • 117
  • 1
  • 4
  • 1
    https://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const – kervin Nov 09 '19 at 04:52

3 Answers3

15

As per Cppreference, the notes on make_shared clearly state that:

std::shared_ptr<T>(new T(args...)) may call a non-public constructor of T if executed in context where it is accessible, while std::make_shared requires public access to the selected constructor.

Since the selected constructor in this case is private, compilation fails.
If you still wish to keep it private use the form std::shared_ptr<T>(new T(args...)) in the static function Create.

EDIT:
If you are intent on using make_shared and also a private constructor, you can employ this hack which uses an empty nested class and an explicit constructor.

A minimal example.

#include <memory>

class Bar : public std::enable_shared_from_this<Bar> 
{

private:
    class Foo {};
    Bar() = default;

public:
    static auto Create() {        
        return std::make_shared<Bar>(Foo());
    }  
    explicit Bar(Foo);    
};

int main()
{
    auto i = Bar::Create();
}

See DEMO.

You can adapt the above to your requirement.

P.W
  • 26,289
  • 6
  • 39
  • 76
  • 3
    @M.M I don't think so, pretty sure `make_shared` will pass construction onto some allocation function that we don't know about (and likely isn't portable to friend even if we did know about it). – Fantastic Mr Fox Jan 31 '19 at 09:27
  • Could not make `std::make_shared` a `friend` but found a hack that will allow the use of `make_shared` with a `private` constructor. – P.W Feb 01 '19 at 05:45
12

Because it is the library function std::make_shared that is invoking private constructor.

user7860670
  • 35,849
  • 4
  • 58
  • 84
0

If you want to avoid using the empty private class constructor argument hack, then you can just construct the shared_ptr from a raw ptr:

#include <memory>
class Bar
{
private:
  Bar() = default;
public:
  static std::shared_ptr<Bar> Create() {
    return std::shared_ptr<Bar>(new Bar);
  };
};