Üye işlevine işlev göstericisi


92

Aynı sınıftaki başka bir işleve işaret eden bir sınıfın üyesi olarak bir işlev işaretçisi ayarlamak istiyorum. Bunu yapmamın nedenleri karmaşık.

Bu örnekte çıktının "1" olmasını istiyorum

class A {
public:
 int f();
 int (*x)();
}

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = a.f;
 printf("%d\n",a.x())
}

Ancak bu, derleme konusunda başarısız. Neden?



@jww ve CiroSantilli'nin bu sorudaki cevabını kontrol edin, diğer cevaplar aşağı yukarı konu dışıdır. Temel olarak, sadece int (C :: * function_pointer_var) (int) = & C :: method; sonra C c; ve (c. * function_pointer_var) (2).
jw_

Yanıtlar:


160

Sözdizimi yanlış. Üye işaretçisi, sıradan bir işaretçiden farklı bir tür kategorisidir. Üye gösterici, sınıfının bir nesnesiyle birlikte kullanılmalıdır:

class A {
public:
 int f();
 int (A::*x)(); // <- declare by saying what class it is a pointer to
};

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = &A::f; // use the :: syntax
 printf("%d\n",(a.*(a.x))()); // use together with an object of its class
}

a.xfonksiyonun hangi nesneye çağrılacağını henüz söylemiyor. Sadece nesnede depolanan işaretçiyi kullanmak istediğinizi söylüyor a. aSol işlenen olarak operandın başına başka bir zaman eklemek .*, derleyiciye işlevi hangi nesnede çağıracağını söyleyecektir.


Bunun eski olduğunu biliyorum ama kullanımını anlamıyorum. (a.*a.x)()Neden çalışmıyor (a.*x)()?
Gaurav Sehgal

3
@gau çünkü x kapsam dahilinde değil
Johannes Schaub - litb

13
Ayrıca her kullandığımda buna bakmam gerekiyor. Sözdizimi kafa karıştırıcıdır, ancak onu parçalarsanız mantıklı olur. a.xA sınıfının üye işlevine bir göstericidir *a.x, göstericinin referansını kaldırır, böylece artık bir işlev başvurusudur. a.(*a.x)işlevi bir örneğe "bağlar" (aynı şekilde a.f). (a.(*a.x))bu karmaşık sözdizimini gruplamak için gereklidir ve (a.(*a.x))()aslında ahiçbir argüman olmadan yöntemi çağırır .
jwm

23

int (*x)()üye işlevi için bir işaretçi değildir. Üye işleve göstericidir böyle yazılır: int (A::*x)(void) = &A::f;.


17

Dize komutunda üye işlevini çağırın

#include <iostream>
#include <string>


class A 
{
public: 
    void call();
private:
    void printH();
    void command(std::string a, std::string b, void (A::*func)());
};

void A::printH()
{
    std::cout<< "H\n";
}

void A::call()
{
    command("a","a", &A::printH);
}

void A::command(std::string a, std::string b, void (A::*func)())
{
    if(a == b)
    {
        (this->*func)();
    }
}

int main()
{
    A a;
    a.call();
    return 0;
}

(this->*func)();İşlev işaretçisini sınıf adıyla bildirmenin yoluna ve yoluna dikkat edinvoid (A::*func)()


11

Sadece bir fonksiyona işaretçi değil, üye fonksiyona işaretçi kullanmanız gerekir.

class A { 
    int f() { return 1; }
public:
    int (A::*x)();

    A() : x(&A::f) {}
};

int main() { 
   A a;
   std::cout << (a.*a.x)();
   return 0;
}

3

Bu, bu sayfadaki başka yerdeki sterlin yanıtlarına dayanıyor olsa da, onlar tarafından tamamen çözülmemiş bir kullanım durumum vardı; fonksiyonlara işaret eden bir vektör için aşağıdakileri yapın:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>

class A{
public:
  typedef vector<int> (A::*AFunc)(int I1,int I2);
  vector<AFunc> FuncList;
  inline int Subtract(int I1,int I2){return I1-I2;};
  inline int Add(int I1,int I2){return I1+I2;};
  ...
  void Populate();
  void ExecuteAll();
};

void A::Populate(){
    FuncList.push_back(&A::Subtract);
    FuncList.push_back(&A::Add);
    ...
}

void A::ExecuteAll(){
  int In1=1,In2=2,Out=0;
  for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){
    Out=(this->*FuncList[FuncId])(In1,In2);
    printf("Function %ld output %d\n",FuncId,Out);
  }
}

int main(){
  A Demo;
  Demo.Populate();
  Demo.ExecuteAll();
  return 0;
}

Buna benzer bir şey, parametre sözdizimi ve yardım ipuçları vb. İle birleştirilmesi gereken dizinlenmiş işlevlere sahip bir komut yorumlayıcısı yazıyorsanız yararlıdır. Menülerde de yararlı olabilir.


1
Tanımlandığı gibi, AFunc, iki girişi alan ve bir giriş vektörü döndüren üye işlevine bir göstericidir . Ancak üyeler int'e dönmeyi işaret ettiler , değil mi? typedef int (A::*AFunc)(int I1,int I2);
Typedef

3

Maalesef mevcut bir üye işlev göstericisini düz bir işlev işaretçisine dönüştüremezken, derleme zamanında bilinen bir üye işlev işaretçisini aşağıdaki gibi normal bir işlevle saran oldukça basit bir şekilde bir bağdaştırıcı işlev şablonu oluşturabilirsiniz:

template <class Type>
struct member_function;

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...)>
{
    template <Ret(Type::*Func)(Args...)>
    static Ret adapter(Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...) const>
{
    template <Ret(Type::*Func)(Args...) const>
    static Ret adapter(const Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

 

int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;

Üye işlevini çağırmak için bir örneğinin sağlanması Agerektiğini unutmayın.


Bana ilham verdin @ IllidanS4. Cevabımı gör. +1
memtha

1

@ IllidanS4'ün cevabını temel alarak, önceden tanımlanmış bağımsız değişkenlere ve sınıf örneğine sahip hemen hemen her üye işlevinin daha sonra arama için referans olarak aktarılmasına izin veren bir şablon sınıfı oluşturdum.



template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs&&... rargs) = 0;
    //virtual RET call() = 0;
};

template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> {
public:
    T * owner;
    RET(T::*x)(RArgs...);
    RET call(RArgs&&... rargs) {
        return (*owner.*(x))(std::forward<RArgs>(rargs)...);
    };
    CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {}
};

template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> {
public:
    T* owner;
    RET(T::*x)(Args...);
    RET call() {
        return (*owner.*(x))(std::get<Args&&>(args)...);
    };
    std::tuple<Args&&...> args;
    CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x),
        args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {}
};

Test / örnek:

class container {
public:
    static void printFrom(container* c) { c->print(); };
    container(int data) : data(data) {};
    ~container() {};
    void print() { printf("%d\n", data); };
    void printTo(FILE* f) { fprintf(f, "%d\n", data); };
    void printWith(int arg) { printf("%d:%d\n", data, arg); };
private:
    int data;
};

int main() {
    container c1(1), c2(20);
    CallbackCreattimeArgs<container, void> f1(&c1, &container::print);
    Callback_t<void>* fp1 = &f1;
    fp1->call();//1
    CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout);
    Callback_t<void>* fp2 = &f2;
    fp2->call();//20
    CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith);
    Callback_t<void, int>* fp3 = &f3;
    fp3->call(15);//20:15
}

Açıkçası, bu sadece verilen argümanlar ve sahip sınıfı hala geçerliyse işe yarayacaktır. Okunabilirlik konusunda ... lütfen beni affet.

Düzenleme: Tuple normal depolamayı yaparak gereksiz malloc kaldırıldı. Referans için devralınan tür eklendi. Bunun yerine tüm argümanları çağrı zamanında sağlama seçeneği eklendi. Şimdi ikisine birden sahip olmaya çalışıyorum ...

Düzenleme 2: Söz verildiği gibi, ikisi de. Yalnızca kısıtlama (gördüğüm), önceden tanımlanmış bağımsız değişkenlerin geri çağrı işlevinde çalışma zamanı tarafından sağlanan bağımsız değişkenlerden önce gelmesi gerektiğidir. Gcc uyumluluğuyla ilgili yardım için @Chipster'a teşekkürler. Bu, ubuntu'da gcc ve pencerelerde visual studio üzerinde çalışır.

#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif

template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs... rargs) = 0;
    virtual ~Callback_t() = default;
};

template<class RET, class... RArgs> class CallbackFactory {
private:
    template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
    private:
        T * owner;
        RET(T::*x)(CArgs..., RArgs...);
        std::tuple<CArgs...> cargs;
        RET call(RArgs... rargs) {
            return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
        };
    public:
        Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
        ~Callback() {};
    };
public:
    template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}
template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
    return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.