【问题标题】:Why doesn't type deduction work for my set intersection and set difference invocations?为什么类型推导对我的集合交集和集合差异调用不起作用?
【发布时间】:2017-12-03 17:19:43
【问题描述】:

我正在尝试编写一个小算法来查找两组的共同和独特部分,我想以通用方式编写它,所以我有这个小例子:

#include "boost/tuple/tuple.hpp"
#include <set>

template <typename InputIt, typename Value = typename std::iterator_traits<InputIt>::value_type>
boost::tuple<std::set<Value>, std::set<Value>, std::set<Value>>
findUniqueAndCommon(InputIt fbegin, InputIt fend, InputIt sbegin, InputIt send)
{
    std::set<Value> setL(fbegin, fend);
    std::set<Value> setR(sbegin, send);

    std::set<Value> uniqueInCatalog1;
    std::set<Value> uniqueInCatalog2;
    std::set<Value> commonInBoth;

    std::set_difference(setL.begin(), setL.end(), setR.begin(), setR.end(), uniqueInCatalog1.begin());
    std::set_difference(setR.begin(), setR.end(), setL.begin(), setL.end(), uniqueInCatalog2.begin());
    std::set_intersection(setL.begin(), setL.end(), setR.begin(), setR.end(), commonInBoth.begin());
    return{ uniqueInCatalog1, uniqueInCatalog2, commonInBoth };
}

int main()
{
     std::set<int> x = {1, 2, 3};
     std::set<int> y = {4, 2, 3};
     findUniqueAndCommon<std::set<int>::iterator>(x.begin(), x.end(), y.begin(), y.end());

}

我的问题是为什么这个函数编译失败?我尝试了 gcc、clang 和 MSVC,但都失败了。可以在此处查看 Clang 的错误消息:
https://godbolt.org/g/gFZyzo

非常感谢。

【问题讨论】:

  • 那个链接只是说&lt;Compilation failed&gt;
  • 如果您将鼠标悬停在显示错误的行上,您会看到错误消息@melpomene
  • 不,我不知道。我得到的只是错误消息中间的一些“注释”。
  • set_* 函数需要OutputIterator 作为结果。 (请随意从您的示例中删除不相关的 boost 用法。)
  • 你忘了#include

标签: c++ c++11 stdset set-operations insert-iterator


【解决方案1】:

原因是 std::set 的常用迭代器 - the one you get with begin() - 不用于插入或删除,仅用于遍历集合中的内容。改用std::inserter

#include "boost/tuple/tuple.hpp"
#include <set>

template <typename InputIt, typename Value = typename std::iterator_traits<InputIt>::value_type>
boost::tuple<std::set<Value>, std::set<Value>, std::set<Value>>
findUniqueAndCommon(InputIt fbegin, InputIt fend, InputIt sbegin, InputIt send)
{
    std::set<Value> setL(fbegin, fend);
    std::set<Value> setR(sbegin, send);

    std::set<Value> uniqueInCatalog1;
    std::set<Value> uniqueInCatalog2;
    std::set<Value> commonInBoth;

    std::set_difference(setL.begin(), setL.end(), setR.begin(), setR.end(), std::inserter(uniqueInCatalog1, uniqueInCatalog1.end()));
    std::set_difference(setR.begin(), setR.end(), setL.begin(), setL.end(), std::inserter(uniqueInCatalog2, uniqueInCatalog2.end()));
    std::set_intersection(setL.begin(), setL.end(), setR.begin(), setR.end(), std::inserter(commonInBoth, commonInBoth.end()));
    return{ uniqueInCatalog1, uniqueInCatalog2, commonInBoth };
}

int main()
{
     std::set<int> x = {1, 2, 3};
     std::set<int> y = {4, 2, 3};
     findUniqueAndCommon<std::set<int>::iterator>(x.begin(), x.end(), y.begin(), y.end());
}

但请注意:

  1. 您正在创建范围的集合副本;我不认为你真的需要这样做。只需使用范围 - set_differenceset_intersection 实际上适用于范围而不是集合。
  2. std::sets 默认保持有序。您是否需要在运行此代码之前和之后订购它们?如果没有,请考虑选择不同的容器。另一方面,正如@n.m 所说,set_differenceset_intersection 需要有序容器。
  3. 您在这两个集合上迭代了 3 次,而不是仅一次(您可以使用自定义函数在有序容器上执行此操作)。
  4. C++ 标准已经有了自己的元组 - std::tuple - 从 C++11 开始。

【讨论】:

  • 非常有效的问题,我需要设置它们,是的,我知道我使用了两次 set_difference。但是传入的套装通常很小(少于 20 件),所以我不太担心它会成为瓶颈。非常感谢您提供高质量的答案。
  • @dorafmon:记住我问这些问题/制作这些 cmets 不仅仅是为了你,也是为了你的问题和我的答案的未来读者。
  • set_difference 和朋友需要排序的范围,所以无序的集合将无法正常工作。
【解决方案2】:

您需要使用 inserter,因为 set 本身的迭代器始终是不允许值修改的 const 迭代器:

... 值类型与键类型相同的关联容器,iteratorconst_iterator 都是常量迭代器

#include <tuple>
#include <set>
#include <algorithm>
#include <iterator>

template <
    typename InputIt, 
    typename Value = typename std::iterator_traits<InputIt>::value_type>
std::tuple<std::set<Value>, std::set<Value>, std::set<Value>> findUniqueAndCommon(InputIt fbegin, InputIt fend, InputIt sbegin, InputIt send)
{
    std::set<Value> setL(fbegin, fend);
    std::set<Value> setR(sbegin, send);

    std::set<Value> uniqueInCatalog1;
    std::set<Value> uniqueInCatalog2;
    std::set<Value> commonInBoth;

    std::set_difference(setL.begin(), setL.end(), setR.begin(), setR.end(), ::std::inserter(uniqueInCatalog1, uniqueInCatalog1.end()));
    std::set_difference(setR.begin(), setR.end(), setL.begin(), setL.end(), ::std::inserter(uniqueInCatalog2), uniqueInCatalog2.end()));
    std::set_intersection(setL.begin(), setL.end(), setR.begin(), setR.end(), ::std::inserter(commonInBoth, commonInBoth.end()));
    return{ uniqueInCatalog1, uniqueInCatalog2, commonInBoth };
}

int main()
{
    std::set<int> x = {1, 2, 3};
    std::set<int> y = {4, 2, 3};
    findUniqueAndCommon<std::set<int>::iterator>(x.begin(), x.end(), y.begin(), y.end());
}

【讨论】:

  • 这个答案缺少解释。为什么不能使用集合作为输出?
  • @dorafmon 我已经更新了我的答案,原来有点搞砸了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多