2012-06-29 13 views
7

Mam tę klasę C++, że jedną dużą, skomplikowaną metodą compute, którą chciałbym nakarmić "kernelem obliczeniowym", metodą tej samej klasy. I postać chciałbym zrobić coś na wzórJak najlepiej przekazać metody do metod tej samej klasy?

class test { 
int classVar_ = 42; 

int compute_add(int a, int b) 
{ 
    compute(int a, int b, this->add_()) 
} 

int compute_mult(int a, int b) 
{ 
    compute(int a, int b, this->mult_()) 
} 


int compute_(int a, int b, "pass in add or multiply as f()") 
{ 
    int c=0; 
    // Some complex loops { 
    c += f(a,b) 
    // } 
    return c; 
} 

int add_(int a, int b){a+b+classVar_;} 
int multiply_(int a, int b){a*b+classVar_;} 
... 

} 

ale nie jestem pewien, jak będę przechodzić w add lub multiply. Alternatywą dla tego podejścia byłoby przekazanie pewnego rodzaju nazwy w celu określenia add() lub multiply(), ale chciałem uniknąć wewnątrz pętli switch lub if.

Jaka jest najlepsza praktyka?

+1

Masz znacznik 'member-function-indexers'. Czy to nie jest dobre miejsce na rozpoczęcie? – chris

+0

@ Chris chyba nie ma pewności co do składni? – RedX

+1

Jeśli metody, które chcesz przekazać, są takie same, jak w twojej próbce, wystarczą proste funkcje (statyczne) i zwykłe wskaźniki funkcji. – Mat

Odpowiedz

5

Jak podejrzewasz, podanie wskaźnika funkcji członka jest dopuszczalną praktyką.

Jeśli trzeba znać składnię, to jest:

int compute_(int a, int b, int (test::*f)(int,int)) 
{ 
    int c=0; 
    // Some complex loops { 
    c += (this->*f)(a,b) 
    // } 
    return c; 
} 

Reprezentowanie funkcji składowych za pomocą liczb całkowitych i przełączanie wprowadza programista napowietrznych do przechowywania rzeczy aż do dnia, w którym lista dostępnych zmian operacyjnych. Więc nie chcesz tego, chyba że jest jakiś ważny powód w konkretnym przypadku.

Jedną z alternatyw jest, aby compute nawet bardziej ogólnie - zamiast podejmowania funkcji składowej, napisać szablon funkcji, które ma każdy rodzaj wpłacone:

template <typename BinaryFunction> 
int compute_(int a, int b, BinaryFunction f) { 
    // body as before but `f(a,b)` instead of `(this->*f)(a,b)` 
} 

Ten bardziej ogólny szablon jest super, jeśli ktoś chce korzystać to z pewnym operatorem własnego wynalazku, który nie jest funkcją członkowską z test. Jest trudniejsze w użyciu w przypadku funkcji członka, ponieważ ktoś musi przechwycić this. Można to zrobić na kilka sposobów - lambda C++ 11, boost::bind lub napisanie londyńskiej gryfowej. Na przykład:

template <typename BinaryFunction> 
int compute_(int a, int b, BinaryFunction f) { 
    // body as before with `f(a,b)` 
} 

int compute_(int a, int b, int (test::*f)(int,int)) 
{ 
    return compute_(a, b, bind_this(f, this)); 
} 

Definiowanie bind_this jest trochę bólu: to jak std::bind1st wyjątkiem tego, że chcielibyśmy pracować z 3-arg funktor natomiast bind1st zajmuje tylko binarny funktor. boost::bind i std::bind w C++ 11, są bardziej elastyczne i będą obsługiwać dodatkowe argumenty.Poniższa zrobi w tym przypadku, ale nie działa w ogóle powiązać funkcje składowe 2-Arg

struct bind_this { 
    int (test::*f)(int,int); 
    test *t; 
    int operator(int a, int b) const { 
     return (t->*f)(a,b); 
    } 
    bind_this(int (test::*f)(int,int), test *t) : f(f), t(t) {} 
}; 

w C++ 11 można po prostu użyć lambda:

int compute_(int a, int b, int (test::*f)(int,int)) 
{ 
    return compute_(a, b, [=](int c, int d){ return (this->*f)(c,d) }); 
} 
+0

Jak nazwałbyś "compute_ (int, int b, int (test :: * f) (int, int))"? "compute_ (1,2, this-> add_)' yields "nie może zainicjować parametru typu" int (test :: *) (int, int) "z wartością typu" "'. 'compute_ (1,2, i this-> add_)' yield' nie może utworzyć niestałego wskaźnika dla funkcji member'. –

+0

@Nico: spróbuj po prostu 'compute_ (1,2, add_)'. – Mat

+0

@Mat Cóż, to jest to samo co 'this-> add_', dodatkowy kwalifikator mówi kompilatorowi gdzie szukać' add_'. –

1

Masz dwie alternatywy:

  1. pomocą pointer to member function
  2. pomocą lambda functions

przykład stosując wskaźnik do funkcji użytkownika:

#include <iostream> 

class D 
{ 
public: 
    D(int v) : classVar_(v){} 
    int add_(int a, int b){return (a+b+classVar_);} 
    int multiply_(int a, int b){return (a*b+classVar_);} 
private: 
    int classVar_; 
}; 

class test { 
public: 

int compute_(int a, int b, D &d, int (D::*f)(int a, int b)) 
{ 
    int c=0; 
    // Some complex loops { 
    c += (d.*f)(a,b); 
    // } 
    return c; 
} 

}; 

int main() 
{ 
    test test; 
    D d(1); 

    std::cout<<"add : " << test.compute_(5, 4, d, &D::add_) << std::endl; 
    std::cout<<"add : " << test.compute_(5, 4, d, &D::multiply_) << std::endl; 
} 

przykład stosując lambda:

#include <iostream> 
#include <functional> 

class D 
{ 
public: 
    D(int v) : classVar_(v){} 
    int add_(int a, int b){return (a+b+classVar_);} 
    int multiply_(int a, int b){return (a*b+classVar_);} 
private: 
    int classVar_; 
}; 

class test { 
public: 

int compute_(int a, int b, std::function< int(int,int) > f) 
{ 
    int c=0; 
    // Some complex loops { 
    c += f(a,b); 
    // } 
    return c; 
} 

}; 

int main() 
{ 
    test test; 
    D d(1); 

    std::cout<<"add : " << test.compute_(5, 4, [&d](int a, int b){ return d.add_(a,b); }) << std::endl; 
    std::cout<<"add : " << test.compute_(5, 4, [&d](int a, int b){ return d.multiply_(a,b); }) << std::endl; 
} 
+0

Czy można połączyć klasy 'test' i' D'? –

+0

@Nico Tak, możesz je scalić. Rozdzielam je, ponieważ sprawia to, że przykład jest bardziej ogólny. –

1

Używaj wskaźniki do funkcji.

int compute(int a, int b, int (test::*f) (int, int)) 
{ 
    int c=0; 
    // Some complex loops { 
    c += (this->*f)(a,b) 
    // } 
    return c; 
} 
Powiązane problemy