在C++中初始化静态的std::map<int, unique_ptr<int>>

4
这是一个类似于这个帖子的问题。我认为最有前途的答案与模板化静态初始化有关。以下是该答案中的类:
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;
    }
};

使用方法:

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

这对于结构体、类和基本类型都非常适用。我想要做的是将这个方法用于值为 unique_ptr<Structure\Class> 的情况,就像这样:
std::map mymap = create_map<DWORD, std::unique_ptr<Structure|Class>>(1, new Structure|Class())(2, new Structure|Class())

我正在尝试使用模板类,以便值可以是任何类型。我从这个帖子中得到了灵感,使用接口作为基类,然后使用模板派生类来保存任何类型的值。因此,这些类看起来像这样:

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

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

那么地图可以按照我之前描述的方式进行设置:

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

但是尝试使用create_map初始化它会失败:
std::map mymap = create_map<DWORD, unique_ptr<MyFieldInterface>>(1, new MyField<DWORD>())(2, new MyField<char>())(3, new MyField<WORD>())

我得到的错误是这样的:
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>*)

所以我认为需要一个构造函数和operator()来正确地处理指针。 我将它们都添加到了类中:

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;
}

我遇到了同样的错误。所以我尝试不使用*

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;
}

我遇到了同样的错误。在写这篇文章时,我意识到问题可能与继承有关,而不一定是create_map的运算符。您能帮助我找出需要使其正常工作的operator()定义或基类/派生类定义吗?
请注意,由于我在工作中不允许使用Boost C++库,请限制您的答案不包括这些库。
编辑:根据T.C.的要求更新了MyFieldInterface。

2
不要在同一个完整表达式中多次使用裸的 new,这是不安全的。此外,MyFieldInterface 需要一个虚析构函数。 - T.C.
1
无论如何,您都需要移动所有内容,因为unique_ptr无法复制,这几乎需要对create_map进行重大手术。 - T.C.
@T.C. 我按照你的要求更新了 MyFieldInterface。我也考虑了你的其他评论。我改用了 std::shared_ptrstd::make_shared,问题得到了很好的解决。错误已经消失了。一旦我有机会测试它,我会把它写成答案。感谢你指引我正确的方向。 - gtrigity
2个回答

2
这是一个可能的实现:
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);
    }
};

请注意通过值传递参数并使用emplace将其移动到地图中,以及从m_map移动的转换运算符。

我不知道MSVC 2012是否支持ref-qualifiers。如果不支持,则需要删除它(即函数参数列表后面的两个&&)。这样做的目的是强制要求create_map只能用作临时变量。也可以强制执行仅调用一次转换运算符,但我没有在上面的代码中这样做。

现在您的调用不能使用裸指针,因为1)它不安全并且2)原始指针不能隐式转换为unique_ptrs。一个简单的make_unique实现不考虑数组是

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)...));
    }
}

您可以将new MyField<DWORD>()更改为util::make_unique<MyField<DWORD>>()*

演示


使用限定调用会禁用 ADL,如果您的调用有参数,在升级编译器时可能会产生意想不到的影响。根据规范,make_unique 的完整实现可以在N3656中的示例代码中找到,这是 make_unique 提案文档。

我在Visual Studio 2017中遇到了“'std :: pair <const _Kty,_Ty> :: pair':函数不接受2个参数”的问题。非常感谢任何编译方面的帮助。 - U. Bulle

0

在T.C.的指导下,加上VC2012没有make_unique函数的事实,我从使用unique_ptr<>改为了使用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>>())

这为我提供了所需的功能,而无需更改底层类。虽然这对我的需求有效,但实际上我将标记T.C.的答案为正确,因为它适用于任何你使用的类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接