【问题标题】:Custom Sorting a vector of tuples自定义对元组向量进行排序
【发布时间】:2014-04-12 12:17:53
【问题描述】:

我有一个像

这样的元组向量
vector<tuple<T1, T2, T3>> v;

我相信,当元组类型的默认比较启动时,它会执行字典比较。

我可以按我选择的元素进行比较吗?例如,上面示例中的第二个元素或包含 m 类型的元组中的第 i 个元素?

提前谢谢你。

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    有很多方法可以做到这一点,我使用的一种归结为声明一个自定义比较对象,实际上如下

    // Functor to compare by the Mth element
    template<int M, template<typename> class F = std::less>
    struct TupleCompare
    {
        template<typename T>
        bool operator()(T const &t1, T const &t2)
        {
            return F<typename tuple_element<M, T>::type>()(std::get<M>(t1), std::get<M>(t2));
        }
    };
    

    适用于任意长度的元组 (避免使用可变参数模板 - 尽管使用可变参数的方式相当容易和安全,因为您可以将 operator() 的参数声明为元组任意长度) 并且适用于对您可以将自定义比较函数/对象传递给它,但使用&lt; 运算符作为默认策略。一个示例用法是

    int main()
    {
        vector<tuple<int, string>> v;
        v.push_back(make_tuple(1, "Hello"));
        v.push_back(make_tuple(2, "Aha"));
    
        std::sort(begin(v), end(v), TupleCompare<0>());
        return 0;
    }
    

    当然还有一种更现代的方法,使用 lambda,所以排序线是

    std::sort(begin(v), end(v), 
        [](tuple<int, string> const &t1, tuple<int, string> const &t2) {
            return get<0>(t1) < get<0>(t2); // or use a custom compare function
        }
    );
    

    我相信为此创建一个函数对象是值得的(避免大量样板代码)并采用第一种方法


    编辑

    随着 Yakk 的评论(关于 c++1y)变得符合标准 (c++14),我们在下面演示 generic lambdas

    的案例
    std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
            return get<0>(t1) < get<0>(t2); // or use a custom compare function
    });
    

    这与TupleCompare 的机制非常匹配,因为operator() 也是模板化的。

    【讨论】:

    • std::tuple_element&lt;M, T&gt;::type 之前缺少typename
    • @Jarod42 Thnx,我更新了帖子(由于某种原因,它在 VS2012 中编译得很好)
    • int? unsignedstd::size_t 可能会提供更好的诊断。哦,请注意 C++1y 使 lambda 案例超级简洁。
    【解决方案2】:

    你可以这样做

    #include <tuple>
    #include <vector>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    
    vector<tuple<int, float, char>> v;
    
    int main() {
      v.push_back(std::make_tuple(1,1.2,'c'));
      v.push_back(std::make_tuple(1,1.9,'e'));
      v.push_back(std::make_tuple(1,1.7,'d'));
    
      sort(v.begin(),v.end(),
           [](const tuple<int,float,char>& a,
           const tuple<int,float,char>& b) -> bool
           {
             return std::get<1>(a) > std::get<1>(b);
           });
    
      cout << std::get<2>(v[0]) << endl;
      return 0;
    }
    

    【讨论】:

      【解决方案3】:

      是的,您可以在需要时在 C++ 中定义自定义排序。我想你需要它来处理std::sort,对吧?查看std::sort 的文档,准确地说是算法的第二个版本,即采用comp 参数的版本。

      你必须定义一个小于函子,像这样:

      struct CustomLessThan
      {
          bool operator()(tuple<T1, T2, T3> const &lhs, tuple<T1, T2, T3> const &rhs) const
          {
              return std::get<1>(lhs) < std::get<1>(rhs);
          }
      };
      

      然后在std::sort中使用:

      std::sort(v.begin(), v.end(), CustomLessThan());
      

      在 C++11 中,您可以通过使用 lambda 而不是创建命名结构来使代码更短。 cppreference.com 上给出的示例也展示了这种技术。

      【讨论】:

      • @AlexanderMills:您指的是三个const 中的哪一个?给运营商自己的?这使得函数const 成为一个很好的实践。
      • 哦哈哈,我不知道你可以用第三个 const 做到这一点
      • @AlexanderMills:一个好的经验法则是默认将所有内容设为const,无论是变量还是函数。只允许在真正需要的地方进行修改。对于具有相似概念的每​​种语言都是如此。不变性使代码更容易理解并且更稳定。例如,在 Java 中,最好将所有内容都设为final,直到/除非需要修改。不幸的是,Java 没有 final 方法。
      • 我很确定 java 有 final 方法,只是当你声明一个类 final 时
      • 哦,没关系,你可以创建最终方法 - docs.oracle.com/javase/tutorial/java/IandI/final.html
      【解决方案4】:

      所以这是我学会如何做的最简单的方法(我认为它比主要答案更直接)。这适用于元组中可以与&lt; 进行比较的任何类型。

      这里是排序函数TupleLess,它利用了一些C风格的语法:

       #include <string>
       #include <vector>
       #include <tuple>      
      
       template<int index> struct TupleLess
       {
          template<typename Tuple>
          bool operator() (const Tuple& left, const Tuple& right) const
          {
              return std::get<index>(left) < std::get<index>(right);
          }
      };
      
      int main(){
          //Define a Person tuple:    
          using Person = std::tuple<std::string, std::string, std::string>;
      
          //Create some Person tuples and add to a vector
          Person person1 = std::make_tuple("John", "Smith", "323 Fake Street");
          Person person2 = std::make_tuple("Sheila", "Henrod", "784 Unreal Avenue");
          Person person3 = std::make_tuple("Mitch", "LumpyLumps", "463 Falsified Lane");
      
          std::vector<Person> vPerson = std::vector<Person>();
          vPerson.push_back(person1);
          vPerson.push_back(person2);
          vPerson.push_back(person3);
      
          //Sort by index at position 2
          std::sort(vPerson.begin(), vPerson.end(), TupleLess<2>());
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-19
        • 2015-11-03
        • 1970-01-01
        • 1970-01-01
        • 2021-08-03
        相关资源
        最近更新 更多