【问题标题】:Why set_symmetric_difference fails to work with comparater?为什么设置 symmetric_difference 无法与比较器一起使用?
【发布时间】:2017-04-20 10:56:13
【问题描述】:
// Example program
#include <iostream>
#include <string>
#include <set>
#include <map>
#include <algorithm>


struct cmp {
   bool operator()(std::string i, const std::pair<std::string,std::string>& p) const
   {
     return i < p.first;
   }

   bool operator()(const std::pair<std::string, std::string>& p, std::string i) const
   {
     return p.first < i;
   }
};

int main(){

     std::set<std::string> s1 {"--name", "--id"};      //Conditionally defined mandatory parameters
     std::map<std::string, std::string> s2 { {"--name","Admin"}, {"--group","Group1"}};      //options given by user

     std::set<std::string> result;

     std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(),
     std::inserter(result, result.end()), cmp());

     std::cout << *result.begin();
}

我希望输出是这样的,如果用户错过了任何一个强制参数,它应该抛出错误,同时如果用户提到除了强制参数之外的任何额外参数,则再次预期错误。

set_symmetric_difference 也无法使用它。

【问题讨论】:

    标签: c++


    【解决方案1】:

    它需要能够比较你给它的任何元素组合,包括字符串到字符串和配对到配对。

    struct cmp {
        bool operator()(const std::string &i1, const std::string &i2)
        {
            return i1 < i2;
        }
    
        bool operator()(const std::pair<std::string, std::string> &p1, const std::pair<std::string, std::string> &p2)
        {
            return p1.first < p2.first;
        }
    
        bool operator()(const std::string &i, const std::pair<std::string, std::string> &p)
        {
            return i < p.first;
        }
    
        bool operator()(const std::pair<std::string, std::string> &p, const std::string &i)
        {
            return p.first < i;
        }
    };
    

    输出:

    --id

    来自文档http://en.cppreference.com/w/cpp/algorithm/set_difference

    比较函数的签名应该等价于:

    bool cmp(const Type1 &a, const Type2 &b);

    签名不需要有const &,但函数对象不能修改传递给它的对象。 Type1 和 Type2 类型必须使得 InputIt1 和 InputIt2 类型的对象可以被取消引用,然后隐式转换为 Type1 和 Type2。 ​

    我只用了一个比较函数,但它不起作用。如果我注释掉任何一个功能,它就不起作用。最后一行似乎意味着两个迭代器都必须隐式转换为两种类型,如果我测试的编译器/标准实现是正确的。但我宁愿只提供所有四种组合。

    【讨论】:

    • 哦,是的,我错过了!同时,您能提示我解决上述问题的可能方法吗?
    • 仍然set_symmetric_difference 没有给出预期的输出,因为这个函数的行为被采用了,它应该返回所有不在两者中的选项,设置以及映射所以这里有问题应该是--id --group
    • 我好像没有编译,你能提供set_symmetric_difference一个代码sn-p吗?
    • s1 和 s2 的迭代器类型不兼容。当我们给 set_difference 一个可以处理所有类型的 cmp 时,这不是问题,但这不适用于 set_symmetric_difference,因为它会尝试将 s2 中的值插入结果。您必须更改 s2 的迭代器返回的类型,这已经在另一个答案中通过 boost 完成,也可以在 c++14 中完成。但是,最简单的解决方案是从地图键中创建一个集合,然后使用它。
    【解决方案2】:

    您编写的代码在我的计算机和on IDEOne 上运行。

    我查看了 C++ 规范工作草案,虽然我没有看到对用户提供的比较器的要求会使行为未定义,但传递给 set_difference 的比较器是与 std::map 和 std::set 的 Compare 模板类型参数不同的类型。

    如果我正在解决这个问题,我会使用 Boost transform_iterator 将用户提供的选项映射迭代器映射到 std::string 键(选项名称)。见Iterate keys in a C++ map。然后我会使用默认比较器 std::less:

    #include <algorithm>
    #include <cassert>
    #include <cstdlib>
    #include <iostream>
    #include <map>
    #include <set>
    #include <string>
    #include <utility>
    
    #include <boost/iterator/transform_iterator.hpp>
    
    int main(){
      std::set<std::string> standard_option_names {"--name", "--id"};
      std::map<std::string, std::string> user_supplied_options { {"--name", "Admin"}, {"--group", "Group1"} };
    
      std::set<std::string> different_option_names;
    
      auto get_option_name = [](const std::pair<const std::string, std::string>& p) -> const std::string& {
        return p.first;
      };
    
      std::set_symmetric_difference(
          standard_option_names.cbegin(), standard_option_names.cend(),
          boost::make_transform_iterator(user_supplied_options.cbegin(), get_option_name), boost::make_transform_iterator(user_supplied_options.cend(), get_option_name),
          std::inserter(different_option_names, different_option_names.end()));
    
      for (const std::string& different_option_name : different_option_names) {
        std::cout << different_option_name;
        if (standard_option_names.find(different_option_name) == standard_option_names.end()) {
          std::cout << " (unknown option)";
        } else {
          assert(user_supplied_options.find(different_option_name) == user_supplied_options.end());
          std::cout << " (mandatory option not supplied)";
        }
        std::cout << '\n';
      }
    
      return EXIT_SUCCESS;
    }
    

    结果:

    --group(未知选项) --id(未提供强制选项)

    【讨论】:

    • 看不懂auto get_option_name = [](const std::pair&lt;const std::string, std::string&gt;&amp; p) -&gt; const std::string&amp; { return p.first; };能详细点吗?
    • @CMouse 它是一个 C++11 lambda,它返回对通过引用传递的 const std::pair&lt;const std::string, std::string&gt;first 成员的引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 2011-12-14
    • 1970-01-01
    • 2016-02-14
    • 1970-01-01
    相关资源
    最近更新 更多