回答:
使用する簡単な例 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が指摘したように、ソート述語を指定する代わりに、operator<
for を実装できますMyStruct
。
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が示唆しているように、>
演算子をオーバーロードし、sortの呼び出しを少し変更することで、ベクトルを降順で並べ替えることもできます。
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>());
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
、次のような逆の順序で並べ替えることができます。
#include <functional>
"std :: greater"を使用する必要があります。
operator<
どちらを使用するかstd::sort(vec.begin(), vec.end());
、std::sort(vec.rbegin(), vec.rend());
昇順または降順のどちらを使用するかによって異なります。
報道のために。ラムダ式を使用した実装を提案しました。
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;
});
>
代わりに使用<
して、降順で取得します。
functorをの3番目の引数として使用するstd::sort
か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() );
}
const
関数シグネチャの最後に追加する必要があるのはなぜですか?
const
。
const
シグネチャの最後にあるキーワードは、operator()
関数がXgreater
構造体のインスタンスを変更しないことを指定します(これは一般にメンバー変数を持つ可能性があります)const
。
vector
タイプのカスタムオブジェクトのそのようなまたはその他の適用可能な(可変入力反復子)範囲の並べ替えは、X
さまざまな方法を使用して、特に次のような標準ライブラリアルゴリズムの使用を含めて達成できます。
X
要素の相対的な順序を取得するためのほとんどの手法は既に投稿されているので、さまざまなアプローチを使用する「理由」と「時期」についてのメモから始めます。
「最良の」アプローチは、さまざまな要因に依存します。
X
オブジェクトの範囲の並べ替えは一般的なタスクですか、それともまれなタスクですか?X
ますか、それともオブジェクトの範囲のソートは絶対確実でなければなりませんか?範囲の並べ替えがX
一般的なタスクであり、達成された並べ替えが期待される(つまりX
、単一の基本値をラップする)場合operator<
、ファズなしで並べ替えを可能にし(適切なコンパレータを正しく渡すなど)、繰り返し期待される結果が得られるため、おそらくオーバーロードになります結果。
並べ替えが一般的なタスクであるか、異なるコンテキストで必要になる可能性が高いが、X
オブジェクトの並べ替えに使用できる基準が複数ある場合は、operator()
ファンクター(カスタムクラスのオーバーロードされた関数)または関数ポインター(つまり、1つのファンクター/関数)を使用します語彙の順序付けと自然順序付けの別の順序)。
タイプのソート範囲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));
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
コンパレータのためではなく、const
(それはメンバ関数ではありませんよう)のキーワードを削除する必要があります。
std::sort
か、または類似していないがCompare
、たとえばstd::set
?
template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
ように使用できますauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
。
あなたは正しい軌道に乗っています。 デフォルトでは比較関数として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 );
ファンクタを使用する利点は、クラスのプライベートメンバーにアクセスできる関数を使用できることです。
operator<
グローバルなメンバーは保護されたメンバーまたはプライベートメンバーを使用できるため、クラス(または構造体)のメンバーを作成する方が適切です。それとも、それ構造体Cの友人にする必要があります
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%のペナルティが発生します。
また、operator <バージョンは〜1%遅くなっているように見えます(テストを複数回繰り返しても効果が持続します)。これは、生成されたコードが異なることを示唆しているため、少し奇妙です(分析するスキルが不足しています--save-温度出力)。
はい、std::sort()
3番目のパラメーター(関数またはオブジェクト)を使用する方が簡単です。例:http :
//www.cplusplus.com/reference/algorithm/sort/
#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;
}
// 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;
}
ユーザー定義のコンパレータクラスを使用できます。
class comparator
{
int x;
bool operator()( const comparator &m, const comparator &n )
{
return m.x<n.x;
}
}
ベクトルをソートするには、の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);
}
比較がfalseの場合、「スワップ」します。