web-dev-qa-db-ja.com

コンパレータとしてメンバー関数を使用した問題のソート

次のコードをコンパイルしようとすると、このコンパイルエラーが発生します。どうすればよいですか?


ISO C++は、非修飾または括弧で囲まれた非静的メンバー関数のアドレスを取得して、メンバー関数へのポインターを形成することを禁じています。

class MyClass {
   int * arr;
   // other member variables
   MyClass() { arr = new int[someSize]; }

   doCompare( const int & i1, const int & i2 ) { // use some member variables } 

   doSort() { std::sort(arr,arr+someSize, &doCompare); }

}; 
27
Navid

doComparestaticでなければなりません。 doCompareMyClassからのデータを必要とする場合、以下を変更することでMyClassを比較ファンクターに変えることができます。

doCompare( const int & i1, const int & i2 ) { // use some member variables } 

bool operator () ( const int & i1, const int & i2 ) { // use some member variables } 

と呼び出し:

doSort() { std::sort(arr,arr+someSize, *this); }

また、doSortには戻り値がありませんか?

std::mem_funとある種のバインディングを使用して、メンバー関数をフリー関数に変えることは可能だと思いますが、正確な構文は現時点では回避できます。

EDIT: Doh、std::sortは、問題になる可能性のある値でファンクターを取得します。これを回避するには、クラス内でファンクターをラップします。

class MyClass {
    struct Less {
        Less(const MyClass& c) : myClass(c) {}
        bool operator () ( const int & i1, const int & i2 ) {// use 'myClass'} 
        MyClass& myClass;
    };
    doSort() { std::sort(arr,arr+someSize, Less(*this)); }
}
28
Andreas Brinck

Andreas Brinckが言うように、doCompareは静的(+1)でなければなりません。 (クラスの他のメンバーを使用して)コンパレーター関数に状態を持たせる必要がある場合は、関数の代わりにファンクターを使用することをお勧めします(これにより高速になります)。

class MyClass{

   // ...
   struct doCompare
   { 
       doCompare( const MyClass& info ) : m_info(info) { } // only if you really need the object state
       const MyClass& m_info;

       bool operator()( const int & i1, const int & i2  )
       { 
            // comparison code using m_info
       }
   };

    doSort() 
    { std::sort( arr, arr+someSize, doCompare(*this) ); }
};

ファンクターを使用する方が常に優れており、入力に時間がかかります(これは不便かもしれませんが、まあ...)

Std :: bindをmember関数と一緒に使用することもできると思いますが、その方法がわかりません。とにかくそれを読むのは簡単ではありません。

UPDATE 2014:今日、c ++ 11コンパイラにアクセスできるので、代わりにラムダを使用できます。コードは短くなりますが、セマンティクスはまったく同じです。

13
Klaim

Robによって提案されたソリューションは現在有効なC++ 11です(Boostは必要ありません)。

void doSort()
{
  using namespace std::placeholders;
  std::sort(arr, arr+someSize, std::bind(&MyClass::doCompare, this, _1, _2));
}

実際、Klaimが述べたように、ラムダはオプションであり、もう少し冗長です(引数がintであることを「繰り返す」必要があります)。

void doSort()
{
  std::sort(arr, arr+someSize, [this](int l, int r) {return doCompare(l, r); });
}

C++ 14はここでautoをサポートします:

void doSort()
{
  std::sort(arr, arr+someSize, [this](auto l, auto r) {return doCompare(l, r); });
}

しかし、それでも、引数はコピーで渡されると宣言しました。

次に、問題は「どちらが最も効率的か」です。その質問はTravisGockelによって処理されました: Lambda vs Bind 。彼のベンチマークプログラムは私のコンピューター(OS X i7)に与えます

                        Clang 3.5    GCC 4.9
   lambda                    1001        7000
   bind                3716166405  2530142000
   bound lambda        2438421993  1700834000
   boost bind          2925777511  2529615000
   boost bound lambda  2420710412  1683458000

ここで、lambdaは直接使用されるラムダであり、lambda boundstd::functionに格納されているラムダです。

したがって、ラムダの方が優れたオプションであるように見えますが、コンパイラーには利益を上げることができる高レベルの情報が提供されているため、それほど驚くことではありません。

8
akim

boost::bind

void doSort() {
  std::sort(arr,arr+someSize, boost::bind(&MyClass::doCompare, this, _1, _2));
}
4
Robᵩ

やりたいことをする方法はありますが、小さなアダプターを使う必要があります。 STLはあなたのためにそれを書かないので、あなた自身でそれを書くことができます:

template <class Base, class T>
struct adaptor_t
{
  typedef bool (Base::*method_t)(const T& t1, const T& t2));
  adaptor_t(Base* b, method_t m)
    : base(b), method(m)
  {}
  adaptor_t(const adaptor_t& copy) : base(copy.base), method(copy.method) {}
  bool operator()(const T& t1, const T& t2) const {
    return (base->*method)(t1, t2);
  }
  Base *base;
  method_t method;
}
template <class Base, class T>
adaptor_t<Base,T> adapt_method(Base* b, typename adaptor_t<Base,T>::method_t m)
{  return adaptor_t<Base,T>(b,m); }

次に、それを使用できます。

doSort() { std::sort(arr,arr+someSize, adapt_method(this, &doCompare)); }
2
PierreBdR

比較は必要ありませんが、less演算子を直接使用できるため、GrahamAsherの回答を更新します。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Qaz {
public:
    Qaz(int aX): x(aX) { }

    bool operator<(const Qaz& aOther) const {
       return x < aOther.x;
    }

int x;
};

int main() {
    std::vector<Qaz> q;
    q.emplace_back(8);
    q.emplace_back(1);
    q.emplace_back(4);
    q.emplace_back(7);
    q.emplace_back(6);
    q.emplace_back(0);
    q.emplace_back(3);
    std::sort(q.begin(),q.end());
    for (auto& num : q)
        std::cout << num.x << "\n";

    char c;
    std::cin >> c;
    return 0;
}
0
Surt

メンバー関数を効果的に使用する非常に簡単な方法は、operator <を使用することです。つまり、compareという関数がある場合は、operator <から呼び出すことができます。これが実際の例です:

class Qaz
{
public:
Qaz(int aX): x(aX) { }

bool operator<(const Qaz& aOther) const
    {
    return compare(*this,aOther);
    }

static bool compare(const Qaz& aP,const Qaz& aQ)
    {
    return aP.x < aQ.x;
    }

int x;
};

そうすれば、std :: sort:に関数名を付ける必要さえありません。

std::vector<Qaz> q;
q.emplace_back(8);
q.emplace_back(1);
q.emplace_back(4);
q.emplace_back(7);
q.emplace_back(6);
q.emplace_back(0);
q.emplace_back(3);
std::sort(q.begin(),q.end());
0
Graham Asher