std::set 插入器不尊重自定义比较器。 (可能的编译器错误?)

Posted

技术标签:

【中文标题】std::set 插入器不尊重自定义比较器。 (可能的编译器错误?)【英文标题】:std::set inserter not respecting custom comparator. (possible compiler bug?) 【发布时间】:2018-05-14 01:41:07 【问题描述】:

我发现std::map<T, C> 上的std::inserter 并不总是尊重自定义比较器,而是有时会退回到默认的operator</std::less。有没有人知道为什么会这样?考虑到零星的性质,我觉得它可能是一个编译器错误。特别是,我有这样的功能:

template <typename T, typename C>
std::set<T, C> operator|(const std::set<T, C> &lhs, const std::set<T, C> &rhs)

  std::set<T, C> out;
  std::set_union(lhs.begin(), lhs.end(),
                 rhs.begin(), rhs.end(),
                 inserter(out, out.end()));
  return out;

但这并不总是使用我的自定义C 比较器,有时(我知道,我也希望我能找到一个模式)只会使用std::less 来代替。如果我用 std::insert_iterator&lt;std::set&lt;T, C&gt;&gt; 的 maunal 构造替换它,一切正常:

template <typename T, typename C>
std::set<T, C> operator|(const std::set<T, C> &lhs, const std::set<T, C> &rhs)

  std::set<T, C> out;
  std::set_union(lhs.begin(), lhs.end(),
                 rhs.begin(), rhs.end(),
                 std::insert_iterator<std::set<T, C>>(out, out.end()));
  return out;

我觉得这可能是一个编译器错误(gcc 版本 7.3.0),因为 http://en.cppreference.com/w/cpp/iterator/inserter 上的可用参考描述了 inserter 以简单地委托给 insert_iterator

template< class Container >
std::insert_iterator<Container> inserter( Container& c, typename Container::iterator i )

    return std::insert_iterator<Container>(c, i);

此外,官方 C++ 标准第 24.5.2.5 节(通过 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf 访问,第 859 页)将插入器的定义设置为返回与第一个参数中传递的类型相同的插入迭代器:

template <class Container>
      insert_iterator<Container> inserter(Container& x, typename Container::iterator i);

这显然不会在这里发生,因为返回的 insert_iterator 是针对 std::set&lt;T, C = std::less&lt;T&gt;&gt; 类型的类,而不是 std::set&lt;T, C&gt;

除了编译器错误,还有其他解释吗?

【问题讨论】:

与您的问题无关:啊,请不要覆盖此类 STL 类型的运算符。 假装它只是说operatorOr,让我继续希望这是 Python... ;) 您正在编译哪个版本的标准(这在这里很重要)? 请发布 MCVE。如果您由于“零星”性质而遇到问题,那么至少可以更好地描述您的问题。例如。你是想说代码随机无法编译吗?如果重试,相同的代码是否会再次编译? “返回的 insert_iterator 用于 std::set> 类型的类”的描述是不可能的,因为这样的迭代器不能引用 set&lt;T, C&gt;。它们是不同的类型。 N3690 是 C++14 之前的草案。最新标准是N4659 【参考方案1】:

“可能的编译器错误”之于 C++ 就像“它可能是狼疮”之于 House MD。

话虽如此,在Documentation 之后,您似乎正在使用该函数的版本 1),其中明确指出:

1) 元素使用运算符

您应该做的是将您的比较器传递给std::set_union,根据该函数的版本 3)

所以真正的谜团是:为什么有时会使用您的比较器。但是,由于您不尊重函数的先决条件:

范围必须相对于相同的范围进行排序。

那么没有定义预期的行为,所以行为不是“不正确的”。

【讨论】:

啊,有道理。因此,从某种意义上说,编译器错误会在它确实起作用时出现,尤其是在我的第二个版本中,我手动构造了一个std::insert_iterator&lt;std::set&lt;T, C&gt;&gt;,它确实起作用了,而且肯定不使用operator&lt; @Jackson,这不是真正的编译器错误,因为您违反了函数的先决条件(“范围必须相对于相同进行排序。”),所以你没有-mans 土地,任何可能产生的行为都是“正确的”。我已经修改了答案来解释它。

以上是关于std::set 插入器不尊重自定义比较器。 (可能的编译器错误?)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Align 时自定义剪裁器不移动

std::set 和 < 运算符重载的奇怪行为?

具有个人比较功能的 std::set 具有相同的值[重复]

X-editable 自定义字段类型不尊重覆盖的默认值

std::set 唯一 ptr 范围插入

插入 std::set<std::tuple<std::string, ...>> 时重复