Initializing a static std::map<int, unique_ptr<int>> in C++

对着背影说爱祢 提交于 2020-01-03 17:06:05

问题


This is a similiar question to this post. The answer that I think has the most promise has to do with templated static initialization. Here is the class from that answer:

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }

    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }

    operator std::map<T, U>()
    {
        return m_map;
    }
};

Usage:

std::map mymap = create_map<int, int >(1,2)(3,4)(5,6);

This works great for a structure or a class as well as the base types. What I would like to do is to use this with a unique_prt<Structure\Class> as the value like this:

std::map mymap = create_map<DWORD, std::unique_ptr<Structure|Class>>(1, new Structure|Class())(2, new Structure|Class())

I am trying to use a template class so that I can have the value be any type. I got the idea from this post to use an interface as a base class and then a template derived class to hold any type of value. So those classes look like this:

class MyFieldInterface
{
public:
    int m_Size;
    virtual ~MyFieldInterface() = default;
}

template <typename T>
class MyField : public MyFieldInterface {
    T m_Value; 
}

Then the map can be setup like I described earlier:

std::map<DWORD, unique_ptr<MyFieldInterface>> mymap;

But trying to initialize it with create_map fails:

std::map mymap = create_map<DWORD, unique_ptr<MyFieldInterface>>(1, new MyField<DWORD>())(2, new MyField<char>())(3, new MyField<WORD>())

The error that I get is this:

operator()
Error: no instance of constructor "create_map<T, U>::create_map [with T=DWORD, U=std::unique_ptr<MyFieldInterface, std::default_delete<MyFieldInterface>>]" matches the argument list
argument types are: (DWORD, MyField<DWORD>*)

So I thought that I need a constructor and an operator() that can handle the pointer properly. I added both to the class:

create_map(const T& key, const U* val)
{
    m_map[key] = val;
}

create_map<T, U>& operator()(const T& key, const U* val)
{
    m_map[key] = val;
    return *this;
}

I got the same error. So I tried without the *:

create_map(const T& key, const U val)
{
    m_map[key] = val;
}

create_map<T, U>& operator()(const T& key, const U val)
{
    m_map[key] = val;
    return *this;
}

I got the same error. As I'm writing this, I realized that the problem may be related to the inheritance and not necessarily create_map's operator. Can you help me figure out the operator() definition or the base/derived class definition that I need to get this to work?

Please limit your answers to not include the Boost C++ libraries as I am not allowed to use them here at work.

Edit: updated MyFieldInterface as requested by T.C.


回答1:


This is one possible implementation:

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(T key, U val)
    {
        m_map.emplace(std::move(key), std::move(val));
    }

    create_map&& operator()(T key, U val) &&
    {
        m_map.emplace(std::move(key), std::move(val));
        return std::move(*this);
    }

    operator std::map<T, U>() &&
    {
        return std::move(m_map);
    }
};

Note the taking argument by value and then moving it into the map with emplace, and the conversion operator that moves from m_map.

I don't know if MSVC 2012 supports ref-qualifiers. If it doesn't, you'll need to remove it (that's the two &&s after the function parameter list). The point of that is to enforce that create_map should only be used as a temporary. It is possible to also enforce that the conversion operator is only called once, but I didn't do that in the code above.

Now your calls cannot use naked news because 1) it isn't exception-safe and 2) raw pointers cannot be implicitly converted to unique_ptrs. A simple make_unique implementation that doesn't take arrays into account is

namespace util {
    template<class T, class... Args>
    std::unique_ptr<T> make_unique(Args&&... args) {
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    }
}

You can then change the new MyField<DWORD>()s to util::make_unique<MyField<DWORD>>()*.

Demo.


* Using a qualified call disables ADL, which can have surprising effects when you upgrade your compiler if your call has arguments. A full implementation of make_unique according to the spec can be found in the example code in N3656, the make_unique proposal paper.




回答2:


With some guidance from T.C. and the fact that VC2012 does not have make_unique in it, I changed from using a unique_ptr<> to a shared_ptr<>.

std::map mymap = create_map<DWORD, shared_ptr<MyFieldInterface>>(1, make_shared<MyField<DWORD>>())(2, make_shared<MyField<char>>())(3, make_shared<MyField<WORD>>())

This gave me the functionality I was looking for without having to change the underlying classes. While this works for my needs, I am actually going to mark T.C.'s answer as correct because it works regardless of which one you use.



来源:https://stackoverflow.com/questions/28901848/initializing-a-static-stdmapint-unique-ptrint-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!