カスタム(ユーザー定義)オブジェクトを含むベクトルの並べ替えについてはどうすればよいですか。
おそらく、標準のSTLアルゴリズムsortをカスタムオブジェクトの(ソートのためのキーとして)フィールドの1つに作用する述語(関数または関数オブジェクト)と共に使用するべきです。利用される。
私は正しい方向に進んでいますか?
std::sort
を使った簡単な例
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};
struct less_than_key
{
inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
};
std::vector < MyStruct > vec;
vec.Push_back(MyStruct(4, "test"));
vec.Push_back(MyStruct(3, "a"));
vec.Push_back(MyStruct(2, "is"));
vec.Push_back(MyStruct(1, "this"));
std::sort(vec.begin(), vec.end(), less_than_key());
編集:Kirill V. Lyadvinskyが指摘したように、ソート述語を提供する代わりに、MyStruct
にoperator<
を実装することができます。
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
bool operator < (const MyStruct& str) const
{
return (key < str.key);
}
};
この方法を使用すると、ベクトルを次のように単純に並べ替えることができます。
std::sort(vec.begin(), vec.end());
Edit2:Kappaが示唆しているように、>
演算子をオーバーロードしてソートの呼び出しを少し変更することで、ベクトルを降順にソートすることもできます。
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
bool operator > (const MyStruct& str) const
{
return (key > str.key);
}
};
そしてあなたはsortを次のように呼ぶべきです:
std::sort(vec.begin(), vec.end(),greater<MyStruct>());
報道のために。 ラムダ式 を使った実装を提案します。
C++ 11
#include <vector>
#include <algorithm>
using namespace std;
vector< MyStruct > values;
sort( values.begin( ), values.end( ), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
return lhs.key < rhs.key;
});
C++ 14
#include <vector>
#include <algorithm>
using namespace std;
vector< MyStruct > values;
sort( values.begin( ), values.end( ), [ ]( const auto& lhs, const auto& rhs )
{
return lhs.key < rhs.key;
});
std::sort
の3番目の引数としてfunctorを使用することも、クラス内でoperator<
を定義することもできます。
struct X {
int x;
bool operator<( const X& val ) const {
return x < val.x;
}
};
struct Xgreater
{
bool operator()( const X& lx, const X& rx ) const {
return lx.x < rx.x;
}
};
int main () {
std::vector<X> my_vec;
// use X::operator< by default
std::sort( my_vec.begin(), my_vec.end() );
// use functor
std::sort( my_vec.begin(), my_vec.end(), Xgreater() );
}
あなたは正しい方向に進んでいます。 std::sort
はデフォルトで比較関数としてoperator<
を使います。そのため、オブジェクトをソートするには、bool operator<( const T&, const T& )
をオーバーロードするか、比較を行う関数を提供する必要があります。
struct C {
int i;
static bool before( const C& c1, const C& c2 ) { return c1.i < c2.i; }
};
bool operator<( const C& c1, const C& c2 ) { return c1.i > c2.i; }
std::vector<C> values;
std::sort( values.begin(), values.end() ); // uses operator<
std::sort( values.begin(), values.end(), C::before );
ファンクターを使用する利点は、クラスの非公開メンバーにアクセスできる関数を使用できることです。
このようなvector
またはその他の適用可能な(変更可能な入力反復子)範囲のX
型のカスタムオブジェクトの並べ替えは、さまざまな方法(特に次のような標準ライブラリアルゴリズムの使用を含む)を使用して実行できます。
X
要素の相対的な順序を取得するためのほとんどのテクニックはすでに公開されているので、さまざまなアプローチを使用するための「なぜ」と「いつ」についてのいくつかのメモから始めます。
「最善」のアプローチは、さまざまな要因によって異なります。
X
オブジェクトの範囲のソートは一般的なタスクですか、それともまれなタスクなのでしょうか(そのような範囲はプログラム内のさまざまな場所またはライブラリのユーザーによってソートされるのですか?)。X
オブジェクトの範囲の並べ替えは絶対確実ですか?X
のソート範囲が一般的なタスクであり、達成されたソートが期待される場合(つまり、X
は基本値を1つだけラップする)、onはoperator<
をオーバーロードすることになります。期待どおりの結果が繰り返し得られます。
ソートが一般的なタスクであるか、または異なるコンテキストで必要とされる可能性が高いが、X
オブジェクトをソートするために使用できる複数の基準がある場合、Functors(カスタムクラスのoperator()
関数のオーバーロード)または関数ポインタ(つまり1つのファンクター) /語彙の順序付けのための/ functionと自然な順序付けのためのもう一つのもの)。
タイプX
の範囲のソートが他のコンテキストでは一般的ではないか、またはありそうもない場合は、より多くの関数またはタイプを持つ名前空間を乱雑にする代わりにラムダを使用する傾向があります。
ソートが何らかの意味で「明確」または「自然」ではない場合、これは特に当てはまります。 operator<
は一見したところでは不透明であるため、その場で適用されるラムダを見ると、順序付けの背後にあるロジックを簡単に理解することができます。
ただし、単一のoperator<
定義は単一障害点であるのに対して、複数ラムバは複数障害点であり、より注意が必要です。
ソートが行われている/ソートテンプレートがコンパイルされている場所でoperator<
の定義が利用できない場合、コンパイラは、オブジェクトを比較するときに順序付けロジックをインライン化するのではなく、関数呼び出しを強制します。リンク時最適化/コード生成が適用されていない場合).
class X
の比較可能性を達成する方法std::vector<X> vec_X;
とstd::vector<Y> vec_Y;
とします
T::operator<(T)
またはoperator<(T, T)
をオーバーロードし、比較関数を必要としない標準ライブラリテンプレートを使用します。メンバーoperator<
をオーバーロードします。
struct X {
int i{};
bool operator<(X const &r) const { return i < r.i; }
};
// ...
std::sort(vec_X.begin(), vec_X.end());
または無料のoperator<
:
struct Y {
int j{};
};
bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
// ...
std::sort(vec_Y.begin(), vec_Y.end());
struct X {
int i{};
};
bool X_less(X const &l, X const &r) { return l.i < r.i; }
// ...
std::sort(vec_X.begin(), vec_X.end(), &X_less);
bool operator()(T, T)
オーバーロードを作成します。struct X {
int i{};
int j{};
};
struct less_X_i
{
bool operator()(X const &l, X const &r) const { return l.i < r.i; }
};
struct less_X_j
{
bool operator()(X const &l, X const &r) const { return l.j < r.j; }
};
// sort by i
std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
// or sort by j
std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
これらの関数オブジェクト定義は、C++ 11とテンプレートを使ってもう少し一般的に書くことができます。
struct less_i
{
template<class T, class U>
bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; }
};
これは、<
をサポートするメンバーi
を持つ任意の型をソートするために使用できます。
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
C++ 14ではさらに一般的なラムダ式が使用可能になります。
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
これはマクロでラップすることができます
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
通常のコンパレータ作成を非常にスムーズにする:
// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));
はい、3番目のパラメータ(functionまたはobject)を指定したstd::sort()
のほうが簡単です。例: http://www.cplusplus.com/reference/algorithm/sort/
あなたのクラスでは、 "<"演算子をオーバーロードすることができます。
class MyClass
{
bool operator <(const MyClass& rhs)
{
return this->key < rhs.key;
}
}
Std :: sortを呼び出すことができるさまざまな方法の間でパフォーマンスに測定可能な影響があるかどうかに興味があったので、この簡単なテストを作成しました。
$ cat sort.cpp
#include<algorithm>
#include<iostream>
#include<vector>
#include<chrono>
#define COMPILER_BARRIER() asm volatile("" ::: "memory");
typedef unsigned long int ulint;
using namespace std;
struct S {
int x;
int y;
};
#define BODY { return s1.x*s2.y < s2.x*s1.y; }
bool operator<( const S& s1, const S& s2 ) BODY
bool Sgreater_func( const S& s1, const S& s2 ) BODY
struct Sgreater {
bool operator()( const S& s1, const S& s2 ) const BODY
};
void sort_by_operator(vector<S> & v){
sort(v.begin(), v.end());
}
void sort_by_lambda(vector<S> & v){
sort(v.begin(), v.end(), []( const S& s1, const S& s2 ) BODY );
}
void sort_by_functor(vector<S> &v){
sort(v.begin(), v.end(), Sgreater());
}
void sort_by_function(vector<S> &v){
sort(v.begin(), v.end(), &Sgreater_func);
}
const int N = 10000000;
vector<S> random_vector;
ulint run(void foo(vector<S> &v)){
vector<S> tmp(random_vector);
foo(tmp);
ulint checksum = 0;
for(int i=0;i<tmp.size();++i){
checksum += i *tmp[i].x ^ tmp[i].y;
}
return checksum;
}
void measure(void foo(vector<S> & v)){
ulint check_sum = 0;
// warm up
const int WARMUP_ROUNDS = 3;
const int TEST_ROUNDS = 10;
for(int t=WARMUP_ROUNDS;t--;){
COMPILER_BARRIER();
check_sum += run(foo);
COMPILER_BARRIER();
}
for(int t=TEST_ROUNDS;t--;){
COMPILER_BARRIER();
auto start = std::chrono::high_resolution_clock::now();
COMPILER_BARRIER();
check_sum += run(foo);
COMPILER_BARRIER();
auto end = std::chrono::high_resolution_clock::now();
COMPILER_BARRIER();
auto duration_ns = std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count();
cout << "Took " << duration_ns << "s to complete round" << endl;
}
cout << "Checksum: " << check_sum << endl;
}
#define M(x) \
cout << "Measure " #x " on " << N << " items:" << endl;\
measure(x);
int main(){
random_vector.reserve(N);
for(int i=0;i<N;++i){
random_vector.Push_back(S{Rand(), Rand()});
}
M(sort_by_operator);
M(sort_by_lambda);
M(sort_by_functor);
M(sort_by_function);
return 0;
}
ランダムベクトルを作成し、それをコピーしてそのコピーをソートするのに必要な時間を測定します(そして、過度に強力なデッドコードの削除を避けるためにチェックサムを計算します)。
G ++(GCC)7.2.1 20170829(Red Hat 7.2.1-1)でコンパイルしていました
$ g++ -O2 -o sort sort.cpp && ./sort
結果は次のとおりです。
Measure sort_by_operator on 10000000 items:
Took 0.994285s to complete round
Took 0.990162s to complete round
Took 0.992103s to complete round
Took 0.989638s to complete round
Took 0.98105s to complete round
Took 0.991913s to complete round
Took 0.992176s to complete round
Took 0.981706s to complete round
Took 0.99021s to complete round
Took 0.988841s to complete round
Checksum: 18446656212269526361
Measure sort_by_lambda on 10000000 items:
Took 0.974274s to complete round
Took 0.97298s to complete round
Took 0.964506s to complete round
Took 0.96899s to complete round
Took 0.965773s to complete round
Took 0.96457s to complete round
Took 0.974286s to complete round
Took 0.975524s to complete round
Took 0.966238s to complete round
Took 0.964676s to complete round
Checksum: 18446656212269526361
Measure sort_by_functor on 10000000 items:
Took 0.964359s to complete round
Took 0.979619s to complete round
Took 0.974027s to complete round
Took 0.964671s to complete round
Took 0.964764s to complete round
Took 0.966491s to complete round
Took 0.964706s to complete round
Took 0.965115s to complete round
Took 0.964352s to complete round
Took 0.968954s to complete round
Checksum: 18446656212269526361
Measure sort_by_function on 10000000 items:
Took 1.29942s to complete round
Took 1.3029s to complete round
Took 1.29931s to complete round
Took 1.29946s to complete round
Took 1.29837s to complete round
Took 1.30132s to complete round
Took 1.3023s to complete round
Took 1.30997s to complete round
Took 1.30819s to complete round
Took 1.3003s to complete round
Checksum: 18446656212269526361
関数ポインタを渡す以外のすべてのオプションは非常によく似ており、関数ポインタを渡すと+ 30%のペナルティが発生します。
演算子<versionが〜1%遅い(私はテストを複数回繰り返しても効果が持続する)ように見えますが、生成されたコードが異なることを示唆しているので少し奇妙です(分析するスキルがありません温度出力)。
下記は lambdas を使ったコードです。
#include "stdafx.h"
#include <vector>
#include <algorithm>
using namespace std;
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};
int main()
{
std::vector < MyStruct > vec;
vec.Push_back(MyStruct(4, "test"));
vec.Push_back(MyStruct(3, "a"));
vec.Push_back(MyStruct(2, "is"));
vec.Push_back(MyStruct(1, "this"));
std::sort(vec.begin(), vec.end(),
[] (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
);
return 0;
}
ユーザー定義のコンパレータクラスを使用できます。
class comparator
{
int x;
bool operator()( const comparator &m, const comparator &n )
{
return m.x<n.x;
}
}
// sort algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
using namespace std;
int main () {
char myints[] = {'F','C','E','G','A','H','B','D'};
vector<char> myvector (myints, myints+8); // 32 71 12 45 26 80 53 33
// using default comparison (operator <):
sort (myvector.begin(), myvector.end()); //(12 32 45 71)26 80 53 33
// print out content:
cout << "myvector contains:";
for (int i=0; i!=8; i++)
cout << ' ' <<myvector[i];
cout << '\n';
system("PAUSE");
return 0;
}
ベクトルをソートするには、のsort()アルゴリズムを使うことができます。
sort(vec.begin(),vec.end(),less<int>());
使用される3番目のパラメータは、より大きくてもより少なくてもよく、または任意の機能またはオブジェクトも使用することができます。ただし、3番目のパラメータを空白のままにした場合、デフォルトの演算子は<です。
// using function as comp
std::sort (myvector.begin()+4, myvector.end(), myfunction);
bool myfunction (int i,int j) { return (i<j); }
// using object as comp
std::sort (myvector.begin(), myvector.end(), myobject);
typedef struct Freqamp{
double freq;
double amp;
}FREQAMP;
bool struct_cmp_by_freq(FREQAMP a, FREQAMP b)
{
return a.freq < b.freq;
}
main()
{
vector <FREQAMP> temp;
FREQAMP freqAMP;
freqAMP.freq = 330;
freqAMP.amp = 117.56;
temp.Push_back(freqAMP);
freqAMP.freq = 450;
freqAMP.amp = 99.56;
temp.Push_back(freqAMP);
freqAMP.freq = 110;
freqAMP.amp = 106.56;
temp.Push_back(freqAMP);
sort(temp.begin(),temp.end(), struct_cmp_by_freq);
}
compareがfalseの場合、「スワップ」します。