【问题标题】:unordered_map of boost::noncopyable can't return references from operator[]boost::noncopyable 的 unordered_map 不能从 operator[] 返回引用
【发布时间】:2012-12-22 08:54:15
【问题描述】:

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

#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 中度过他们的整个生命周期。

【问题讨论】:

  • 在将项目移入哈希表的过程中,复制是您请求的操作的本质所固有的。执行insert() 也会遇到完全相同的问题。我不知道有人甚至可以拥有带有不可复制实体的map&lt;&gt;unordered_map&lt;&gt;(就此而言,map&lt;&gt; 的密钥)。如果不是这样,我有点想了解自己。 emplace() 能满足你的需求吗?
  • 你是对的。从 boost::noncopyable 继承时,似乎甚至无法将项目添加到 unordered_map。
  • 查看emplace() 家族。它可能会做你正在寻找的东西。祝你好运。
  • 我删除了我的答案,因为我认为这可能是一个库错误。标准说operator[] 只要求映射类型是默认可构造的,而不是复制或移动构造的。您可以有一个地图,您只需默认使用operator[] 构造所有元素,然后再修改它们。
  • @WhozCraig 遗憾的是没有 :(。Emplace() 没有可操作的移动构造函数,尽管从概念上讲这正是我想要做的。我不想复制这些对象。我只是想要他们住在 unordered_map 中,但这似乎是不可能的。

标签: c++ boost


【解决方案1】:

unordered_map 中使用boost::noncopyable 的子类并非不可能,您只需为您的类型定义一个移动构造函数。如果您创建了自己的复制构造(boost::noncopyable 所做的),C++ 不会创建默认移动构造函数。此外,如果它确实定义了默认移动构造函数,它会尝试调用父级的私有复制构造函数。因此,您必须定义一个不会尝试调用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;
}

【讨论】:

  • 我会将此标记为解决方案,因为它解决了一般性讨论。它仍然不允许我制作 boost::signals2::signal 的 unordered_map,因为该对象没有定义移动构造函数:(。这似乎是信号库设计中的疏忽(或者它需要更新对于 C++11)。
【解决方案2】:

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

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

最后,请注意这不是试图解决 OP 使用operator []() 进行插入+访问的请求。相反,它试图简单地将boost::noncopyable 派生构造成unordered_map&lt;&gt;。要访问,您可能需要以下组合以及初始 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 2016-08-31
    相关资源
    最近更新 更多