简体   繁体   English

为什么类型推导不适用于我的集合相交和集合差异调用?

[英]Why doesn't type deduction work for my set intersection and set difference invocations?

I am trying to write a small algorithm that finds the common and unique parts of two sets and I want to write it in a generic fashion so I have this little example: 我正在尝试写一个小的算法来找到两个集合的公共和唯一部分,我想以一种通用的方式编写它,所以我有一个小例子:

#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());

}

My question is that why does this function fail to compile? 我的问题是为什么该函数无法编译? I tried gcc, clang and MSVC and they all fail. 我尝试了gcc,clang和MSVC,但它们都失败了。 Clang's error message can be inspected here: 可以在此处检查Clang的错误消息:
https://godbolt.org/g/gFZyzo https://godbolt.org/g/gFZyzo

Thanks a lot. 非常感谢。

You need to utilize inserter because iterators of set itself are always const iterators that don't allow value modification: 您需要利用inserter因为set本身的迭代器始终是不允许值修改的const迭代器:

... associative containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators ...值类型与键类型相同的关联容器, 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());
}

The reason is that std::set 's usual iterator - the one you get with begin() - is not intended for insertion or deletion, only for traversal of what's in the set. 原因是std::set的常用迭代器( 使用begin()获得的迭代器begin()不打算插入或删除,仅用于遍历集合中的内容。 Try an std::inserter instead: 尝试使用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());
}

Note, though, that: 但是请注意:

  1. You're creating set copies of ranges; 您正在创建范围的集合副本; I don't think you really need to do that. 我认为您确实不需要这样做。 Just use the ranges - set_difference and set_intersection actually work on ranges rather than sets. 只需使用范围set_differenceset_intersection实际上可以在范围而不是集合上工作。
  2. std::set s are kept in-order by default. 默认情况下, std::set保持顺序 Do you need them to be ordered before and after running this code? 在运行此代码之前和之后,是否需要订购它们? If not, consider a different choice of containers. 如果没有,请考虑选择其他容器。 On the other hand, as @nm notes, an ordered container is necessary for set_difference and set_intersection . 另一方面,如@nm所示,对于set_differenceset_intersection必须使用有序容器。
  3. You're iterating over both sets three times instead of just once (which you would with a custom function for doing this on ordered containers). 您要遍历两个集合而不是一次来遍历这三个集合(您可以使用自定义函数在有序容器上执行此操作)。
  4. The C++ standard has had its own tuple - std::tuple - since C++11 already. 自C ++ 11起,C ++标准就有了自己的元组-std :: tuple

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM