8

为了演示我的问题,请考虑这个无法编译的简单程序:

#include <boost/noncopyable.hpp>
#include <unordered_map>

class foo : boost::noncopyable { };

int main()
{
    std::unordered_map<int, foo> m;
    auto & element = m[0];

    return 0;
}

使用当前版本的 boost (1.52),Visual Studio 2012 返回错误: cannot access private member declared in class 'boost::noncopyable_::noncopyable.

std::unordered_map 的运算符 [] 返回对所提供键处元素的引用,乍一看似乎应该可以工作——我要求的是对元素的引用,而不是它的副本。

我对这个问题的理解是这样的(这可能是错误的,因为我有一段时间没有使用 C++)。如果未找到该键,unordered_map 将创建一个新元素并返回对新元素的引用。boost::noncopyable 定义了一个(私有)复制构造函数,但没有定义一个移动构造函数,因此编译器不会生成移动构造函数。在其 operator[] 中,std::unordered_map 使用了 std::move,但由于 boost::noncopyable 没有定义移动构造函数,所以它回退到复制构造函数。由于复制构造函数是私有的,因此编译失败。

促使这篇文章的原因是我试图创建一个 boost::signal2::signal 的 unordered_map,它继承自 boost::noncopyable。除了破解 boost 库,我能做一个简单的解决方法吗?将信号包装在 unique_ptr 中是一种选择,但在我看来,我可能在这里做错了什么。

更新:

我可能发布得太早了!似乎不可能将 boost::noncopyable 的子类添加到 unordered_map。Insert、operator[] 和 emplace 都使用复制构造函数(私有)或移动操作(对于 boost::noncopyable 不存在)。对我来说,这似乎是一个主要限制。甚至可以创建一个包含 boost::noncopyable 对象的 unordered_map 吗?我明确地不想复制它们——我希望它们在 unordered_map 中度过他们的整个生命周期。

4

2 回答 2

7

boost::noncopyable使用in 的子类并非不可能unordered_map,您只需为您的类型定义一个移动构造函数。如果您创建了自己的复制构造(就是这样做的),C++ 不会创建默认的移动构造函数boost::noncopyable。此外,如果它确实定义了默认移动构造函数,它会尝试调用父级的私有复制构造函数。因此,您必须定义一个不尝试调用boost::noncopyable的复制构造函数的移动构造函数。例如,这很好用:

#include <boost/noncopyable.hpp>
#include <unordered_map>

struct foo : public boost::noncopyable
{
    foo() = default;
    foo(foo&&) {}
};

int main()
{
    std::unordered_map<int, foo> m;
    auto & element = m[0];

    return 0;
}
于 2012-12-22T09:36:33.580 回答
1

这可能不是您想要的,但我想我会把它扔在那里。需要注意的一点是secondfrom 的返回对的值emplace(),这表明第二次调用不会引入新成员,也不会复制现有成员。

同样,我不知道这是否更接近您想要的,但值得一试。我可能做错了什么,因为我不像其他人那样熟悉 C++11 标准库。如果是这样,我很抱歉。

最后,请注意,这并不是试图解决 OP 使用operator []()插入+访问的请求。相反,它试图简单地将boost::noncopyable推导构造成unordered_map<>. 要访问,您可能需要结合以下内容以及首字母find()来确定标签最初是否存在。

反正...

#include <boost/noncopyable.hpp>
#include <iostream>
#include <unordered_map>

class Foo : public boost::noncopyable
{
public:
    Foo(int value) : value(value) {};

    void setValue(int value) { this->value = value; }
    int getValue() const { return value; }

private:
    int value;
};


int main(int argc, char *argv[])
{
    typedef std::unordered_map<std::string, Foo> MyMap;
    MyMap mymap;

    // throw ("test".1) into the map
    auto p = mymap.emplace("test", 1);
    auto q = mymap.emplace("test", 2); // should not overwrite the first.

    // dump content
    cout << p.first->second.getValue() << '(' << p.second << ')' << ' '
         << q.first->second.getValue() << '(' << q.second << ')' << endl;

    // modify through the second returned iterator/bool pair.
    q.first->second.setValue(3);

    // dump again, see if p was also updated.
    cout << p.first->second.getValue() << '(' << p.second << ')' << ' '
         << q.first->second.getValue() << '(' << q.second << ')' << endl;

    return 0;
}

输出

1(1) 1(0)
3(1) 3(0)
于 2012-12-22T10:02:10.253 回答