【问题标题】:Implementing a map_keys_iterator by derivation: a single compiler error通过派生实现map_keys_iterator:单个编译器错误
【发布时间】:2010-09-05 06:17:56
【问题描述】:

我在周末再次使用 C++ 并注意到一些我不确定它来自哪里的东西。

Following the advice in this thread,我决定实现一个map_keys_iteratormap_values_iterator。我采取了 - 我认为 - 推荐的方法,即从 std::map<K,V>::iterator 派生一个类并这样实现它:

template <typename K, typename V>
struct map_values_iterator:
public std::map<K,V>::iterator {
  // explicitly call base's constructor
  typedef typename std::map<K,V>::iterator mIterator;
  map_values_iterator (const mIterator& mi) : 
  mIterator(mi) {};

  const V& operator* () const { return (*this)->second; }
};

到目前为止,一切顺利,以下代码有效(nvm Unicode,我默认使用支持 i18n 的终端):

typedef std::map<double,string> Map;
Map constants;
constants[M_PI] = "π";
constants[(1+sqrt(5))/2] = "φ";
constants[exp(M_PI)-M_PI] = "fake_20";
// ... fill map with more constants!
map_values_iterator<double, std::string> vs(constants.begin());
for (; vs != m.end(); ++vs) { 
    cout<< (vs != m.begin() ? ", " : "")<< *vs;
    }
cout<< endl;

这段代码打印出预期的结果,类似于(因为地图的元素是有序的):

..., φ, ..., π, ...., fake_20, ....

所以我猜map_keys_iterator 也会以类似的方式工作。我注意到 Map 的 value_type 实际上是 pair&lt;const K, V&gt;,所以 keys 版本会返回一个值。

然而,必须声明迭代器的类型很笨拙,所以我想用经典的make_pair-like 习惯用法创建一个调用者。这就是麻烦开始的地方:

template <typename K, typename V>
map_values_iterator<K,V> map_values(const typename std::map<K,V>::iterator &i) {
    return lpp::map_values_iterator<K,V> (i);
}

template <typename K, typename V>
map_values_iterator<K,V> map_values(const typename std::map<K,V>::const_iterator &i) {
    return lpp::map_values_iterator<K,V> (i);
}

我比较确定这个函数有正确的签名和构造函数调用。但是,如果我尝试从代码中调用该函数:

auto vs= map_values(constants.begin());

我得到一个单一的 STL 编译器错误,格式如下:

error: no matching function for call to ‘map_values(std::_Rb_tree_iterator<std::pair<const double, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)’

我在这里假设在这种特殊情况下,整个_Rb_tree_iterator 实际上是正确的迭代器,map::iterator 是对其进行类型定义的;不过我不完全确定。我尝试提供更多重载以查看其中一个是否匹配(删除引用、删除 const、仅使用 non-const_iterator 变体等),但到目前为止,没有任何东西允许我感兴趣的签名。

如果我在调用函数之前将基本迭代器存储在变量中(如auto begin= constans.begin(); auto vs= map_values(begin);),我会得到完全相同的基本错误,只有不匹配调用的描述明显不同(因为它是“const blah& ")。

我第一次尝试实现这种迭代器是通过创建一个聚合 map::iterator 而不是继承的基类,并派生两个类,每个类都有足够的operator*,但该版本遇到的问题比上面,仍然迫使我复制太多的界面。所以我尝试了这个选项进行代码探险。

我试图寻找这个问题的答案,但我的 Google-fu 今天不是很强大。也许我遗漏了一些明显的东西,也许我忘记了推导的东西(尽管我几乎可以肯定我没有——迭代器不像容器),也许我实际上需要为地图指定所有模板参数,或者也许我的编译器坏了,但不管它是什么,我都找不到它,而且我很难理解编译器在这里抱怨的实际是什么。根据我之前的经验,如果您对 STL 做错了什么,您应该会看到大量错误,而不仅仅是 一个(这不是 STL 启动)。

所以...任何(封装良好的)指针都会受到赞赏。

【问题讨论】:

  • 你需要使用你自己的实现吗(你有什么自定义的吗?)因为如果这个网站上还没有一个(我想我可能已经看到了),我应该有使用 boost::iterator_adaptor 的 map_key_iterator 的实现(非常基本),它可能会起作用。我在自动转换(到普通 map_iterator)时遇到了一些问题,我无法使用当前的 c++ 解决,所以我没有费心完全完成它,但如果你认为它可能会让你感兴趣,我可以尝试发布代码。
  • 不,我当然不需要我的 自己的 实现,但它可以作为练习(我已经离开 C++ 大约 4 年了)并且是一个很好的通用的东西放在自己的工具箱中(Pavel 在下面给出了关键提示)——特别是对于 Boost 不可用的情况。

标签: c++ templates map iterator


【解决方案1】:

原因是您的 KV 类型参数位于不可推导的上下文中,因此您的函数模板甚至在重载解析期间都不会实例化。

再看一遍:

template <typename K, typename V>
map_keys_iterator<K,V> map_keys(const typename std::map<K,V>::iterator &i)

为此,C++ 编译器必须以某种方式从特定的iterator 类转到它的“父容器”类型——在本例中为map——以获取它的KV。一般来说,这是不可能的——毕竟,特定的iterator 可能是某个其他类的 typedef,而调用中的实际参数类型是其他类;编译器无法“追溯”它。因此,根据 C++ 标准,它甚至不会在这种情况下尝试 - 更一般地说,在任何情况下,你有 typename SomeType&lt;T&gt;::OtherType,而 T 是一个类型参数。

您可以做的是使整个参数类型成为模板类型参数。不过,这需要一些技巧来派生 KV

template <typename Iterator>
map_keys_iterator<
     typename std::iterator_traits<Iterator>::value_type::first_type,
     typename std::iterator_traits<Iterator>::value_type::second_type
> map_keys(Iterator i)

不幸的是,在调用您的类型的构造函数时,您还必须在函数体中重复这两个。

附带说明,迭代器通常按值传递(它们是轻量级的)。

【讨论】:

  • 啊哈....所以如果我理解正确的话,这是“一个额外的间接层”问题,对吧? I 可以上下组合表达式的类型,但编译器只能下降。我没想过检查iterator_traits 并看到它提供了对数据类型的访问。一个很好的。我在一夜之间检查了它,它允许我创建一个map_keys_iterator,它在第一次尝试使用 map、multimap 和 unordered_map 时就可以工作,所以我现在很高兴。
  • 在更一般的情况下,您也不能真正上下移动。例如,在这种情况下,参数的实际类型——从你的错误信息来看——是std::_Rb_tree_iterator。现在想象一下,你所拥有的就是这个,并尝试从逻辑上推断出关联的std::map 类型,并从中推断出KV,无需猜测:) 更不用说我总是可以把扳手扔进去在那里,例如专门针对 map 的特定实例化以重用来自不同迭代器类型的迭代器类型 - 因此对于某些迭代器类型将有多个“父”映射类型......
  • 好的。这清楚了很多。修复了代码,它的工作原理如上所述。我会发布它,但我确信它会比 boost::something 带来的“相同但更少”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-17
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多