【问题标题】:How did I manage to store a reference type in a standard container?我是如何设法将引用类型存储在标准容器中的?
【发布时间】:2014-08-30 13:46:27
【问题描述】:

在尝试使用 C++11 智能指针和容器类时,我编写了两段代码,基本上可以归结为以下内容:

class Foo { ... };
typedef std::unique_ptr<Foo> FooPtr;

// First attempt; this did _not_ compile
typedef std::unordered_map<int, FooPtr> IntToFooMap;

// Second attempt; this _did_ compile
typedef std::unordered_map<int, const FooPtr &> IntToFooMap;

// here is the declaration causing the error in the second case:
IntToFooMap m {
    { 41, FooPtr(new Foo()) }
};

如您所见,首先我尝试定义一个未编译的FooPtr 的无序映射,因为FooPtr 是一个不可复制的std::unique_ptr(它有一个已删除的复制构造函数)。

更准确地说,是使用初始化列表初始化地图导致错误。

然而,令我大吃一惊的是,第二个版本,我将无序映射的value_type 定义为FooPtrconst 引用,确实编译了。

直到今天,我认为一般情况下不可能将引用类型的值存储在标准容器中。虽然这种行为在某种程度上是可以直观理解的,但由于声明 (const) 引用可以防止 - 否则禁止 - 复制 unique_ptr,我仍然不完全理解它是如何工作或为什么工作的。 如果它确实有效,当然可以;如果这只是导致未定义行为的微妙极端情况,我不会感到惊讶。

【问题讨论】:

  • 请注意,不可复制构造并不一定意味着不能将类型存储在容器中。现在 C++11 具有移动语义,标准容器可以存储可移动但不可复制的类型。
  • typedef std::unordered_map&lt;int, FooPtr&gt; IntToFooMap; 在 g++ 4.8.2 上编译良好​​span>
  • @Slava 作为记录,我使用的是 clang 3.4。
  • @user3477950 Can't reproduce 要么。
  • @user3477950 您的第二种情况导致引用悬空。 Note how 在这种情况下,Foo 在控制到达下一条语句之前被销毁。

标签: c++ c++11 reference copy-constructor


【解决方案1】:

您违反了库的约束。这会导致未定义的行为。该库可能会也可能不会检测到它是参考。

引用的问题是允许库假定容器拥有保存元素值的对象,因此调用其析构函数,特别是它可以这样做:

allocator_traits<A>::destroy(m, p) // m is the allocator, p points to object.

我没有注意到语言明确要求 std::[unordered_]map 中的映射类型是对象类型,但是您必须非常小心地避免 operator[] 尝试默认构造 value_typestd::pair&lt;int, FooPtr const &amp;&gt;。对于无序的容器,还需要value_type的CopyAssignability,为了使用动态数组作为散列桶,虽然引用是CopyAssignable(复制传递给被引用的对象),但包含引用的pair则不是。

我怀疑图书馆的意图是允许在那里引用,但可能缺少禁令。反正我现在很困。也许有人能找到它。

【讨论】:

  • 感谢您的解释。所以基本上,我的错误是我创建了一个我拥有的对象(智能指针),但它是一个临时对象——“但是你必须非常小心地避免 operator[] 试图默认构造 @987654328 @" - 仅供参考,实际代码中的这张地图是static const,所以我不会在上面调用operator[],只会调用::at()。您能否提出一个替代方案,以便我可以使用unique_ptrs 初始化我的地图?
  • @user3477950 对于基于initializer_list 的构造函数,该标准要求value_typeEmplaceConstructible 来自*il.begin(),不幸的是这对于pair&lt;const int, unique_ptr&lt;...&gt;&gt; 是不正确的。
  • @T.C.阿哈姆,所以那是不可能的。 :/ 谢谢。
  • @user3477950 您可以将unique_ptr 包装为一个类中的mutable 成员,该类具有来自unique_ptr 的移动转换构造函数和返回unique_ptr 的移动隐式转换。 (两种类型都是按值,而不是引用。)不要告诉任何人我告诉过你,这是一个 hack。
  • @Potatoswatter 谢谢,但我不打算退回到黑客,即使标准库有缺陷;)我最终使用作业来填充地图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多