4

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.

3
  • 2
    Don't use multiple naked news in the same full expression. It isn't exception safe. Also MyFieldInterface needs a virtual destructor. Commented Mar 6, 2015 at 15:26
  • 1
    In any event, you'll need to move everything, because unique_ptr cannot be copied, which pretty much requires major surgery on create_map. Commented Mar 6, 2015 at 15:31
  • @T.C. I updated MyFieldInterface as you requested. I've also thought about your other comments. I changed to using a std::shared_ptr and std::make_shared. That resolves the problem nicely. Error has gone away. Once I've had a chance to test it, I'll write it up as the answer. Thank you for pointing me in the right direction. Commented Mar 6, 2015 at 16:02

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I'm getting "'std::pair<const _Kty,_Ty>::pair': function does not take 2 arguments" in Visual Studio 2017. I really appreciate any help for compiling this
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.