【问题标题】:Does std::map<K,V>::iterator instantiate std::map<K,V>?std::map<K,V>::iterator 是否实例化 std::map<K,V>?
【发布时间】:2019-07-30 14:20:28
【问题描述】:

我有这段代码,它适用于 GCC:

#include <map>

class Foo;
class Bar;

typedef std::map<Foo,Bar> MyMap;

MyMap::iterator i;

class Foo
{
    MyMap::iterator some_data;
};

当前设计的代码(这是令人不快的循环,是的,我坚持使用它)要求 map&lt;Foo,Bar&gt;::iterator 可用于 FooBar

之所以有效,是因为 GCC 库实现恰好不需要实例化映射的键类型来实例化迭代器。

这是有保证的吗?在定义映射迭代器类型时,该标准似乎有些放任自流。这段代码的可移植性如何?

【问题讨论】:

  • 我将采用最新标准所说的任何内容。
  • @Yakk-AdamNevraumont 你的意思是this 表示我的代码不符合标准吗?
  • typedef std::map&lt;Foo,Bar&gt; MyMap; 已经是非法的。
  • @Yakk-AdamNevraumont 为什么?那里没有实例化发生。

标签: c++ templates iterator stdmap c++-standard-library


【解决方案1】:

这会导致未定义的行为。

在声明MyMap::iterator i;中,MyMap需要是一个完整的类型,因此它被隐式实例化。然而FooBar在这个实例化点上并不完整,所以根据[res.on.functions]/2行为是未定义的:

特别是在以下情况下效果是不确定的:

  • ...
  • 如果在实例化模板组件或评估概念时将不完整的类型 ([basic.types]) 用作模板参数,除非该组件特别允许。

【讨论】:

    【解决方案2】:

    您可以通过考虑std::map 是基于节点的容器这一事实来回避整个问题,因此其包含元素的节点具有稳定的地址。 IE。您可以使用普通指针而不是迭代器(当然,只要您不需要迭代器来传递给std::map 的成员函数):

    class Foo
    {
        std::pair<Foo const, Bar>* some_data;
    };
    

    请注意,此处仅需要 FooBarstd::pair&lt;&gt;声明来定义成员 some_data

    如果您使用boost::multi_index(在许多方面都优于std 关联容器),那么它具有非常有用的函数to_iterator,它可以引用元素并返回一个迭代器。

    【讨论】:

    • 这不只是将问题转移到std::pair 是否可以用不完整的类型实例化吗?
    • @user463035818 std::pair&lt;&gt; 未在 std::pair&lt;&gt;* 中实例化。就像您可以通过仅包含其前向声明 #include &lt;iosfwd&gt; 来声明和传递 std::ostream&amp; 一样。
    猜你喜欢
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 2011-04-21
    • 1970-01-01
    • 2019-01-23
    • 1970-01-01
    • 2018-11-23
    • 2013-12-20
    相关资源
    最近更新 更多