Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I access the private constructor in my static function as below?

Tags:

c++

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_() {}
like image 946
Jimmy Su Avatar asked Jan 31 '19 06:01

Jimmy Su


2 Answers

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.

like image 52
P.W Avatar answered Oct 26 '22 19:10

P.W


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

like image 41
user7860670 Avatar answered Oct 26 '22 17:10

user7860670