为什么std :: is_permutation不能在两种不同类型的数据之间起作用?

时间:2016-08-03 14:24:05

标签: c++11 lambda stl

假设我有一个整数和字符串向量,我想比较它们是否具有等效元素,而不考虑顺序。最后,我问的是整数向量是否是字符串向量的排列(反之亦然)。我希望能够只调用is_permutation,指定一个允许我比较两者的二元谓词,然后继续我的生活。例如:

bool checkIntStringComparison( const std::vector<int>& intVec,
                 const std::vector<std::string>& stringVec,
                 const std::map<int, std::string>& intStringMap){
  return std::is_permutation<std::vector<int>::const_iterator, std::vector<std::string>::const_iterator>(
        intVec.cbegin(), intVec.cend(), stringVec.cbegin(), [&intStringMap](const int& i, const std::string& string){
    return string == intStringMap.at(i);
  });
}

但是尝试编译它(在gcc中)会返回一条错误消息,该消息可归结为:

  

stuff ::&lt;的调用不匹配lambda(const int&amp;,const string&amp;&gt;)(const std :: _ cxx11 :: basic_string&amp;,const int&amp;)

看看它如何从lambda中切换调用签名?如果我切换它们,签名会以另一种方式切换。

关于这个错误,似乎标准为std :: is_permutation指定了ForwardIterator1和2必须是同一类型。所以我理解这方面的编译器错误。但为什么要这样呢?如果我提供一个二进制谓词,允许我比较两者(或者如果我们之前在两者之间定义了一些相等运算符?),那么只是搜索容器1以确保其所有元素都是在容器2中唯一?

1 个答案:

答案 0 :(得分:3)

问题是元素可能不止一次出现。这意味着谓词不仅需要能够将第一个范围的元素与第二个范围的元素进行比较,还要将第一个范围的元素与自身进行比较:

if (size(range1) != size(range2))
    return false;
for (auto const& x1 : range1)
    if (count_if(range1, [&](auto const& y1) { return pred(x1, y1); }) !=
        count_if(range2, [&](auto const& y2) { return pred(x1, y2); }))
        return false;
return true;

由于创建一个带有两个不同签名的函数对象相对棘手,并且传递两个谓词会令人困惑,最简单的选择是指定两个范围必须具有相同的值类型。

您的选择是:

  • 在提供相同值类型的转换中包裹一个范围(或两者)(例如,使用Boost.Adaptors.Transformed);
  • 编写自己的std :: is_permutation实现(例如,复制cppreference上的示例实现);
  • 实际上,请注意gcc(即libstdc ++)实现并不强制值类型相同;它只需要几个你必须提供的签名,所以写一个多态谓词,例如,一个函数对象或一个多态lambda,或者可以从两个范围值类型转换的参数类型(例如在你的情况下boost::variant<int, string> - 丑陋,但可能没那么糟糕)。这是不可移植的,因为另一个实现可能会选择强制执行该要求。