Soru Ücretsiz bir fonksiyonun beklendiği bir üye fonksiyonunu nasıl geçebilirim?


Soru şu: Bu kod parçasını düşünün:

#include <iostream>


class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a();

    function1(&test);
    function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?

    return 0;
}

Nasıl kullanabilirim a'ler aClass::test bir argüman olarak function1? Bunu yaparken sıkışıp kaldım.

Derse üye olmak istiyorum.


76
2017-09-30 16:28


Menşei


Bu cevaba bir göz at stackoverflow.com/questions/2402579/...ve ayrıca bu C ++ SSS parashift.com/c++-faq/pointers-to-members.html - amdn
Bu kesinlikle bir kopya değildir (en azından bağlantılı olan özel sorudan değil). Bu soru, bir işleve bir işaretçi olan bir üyenin nasıl bildirileceğidir; Bu, bir gösterici olmayan statik üye işlevine parametre olarak nasıl iletilir. - CarLuva


Cevaplar:


İşlev göstergelerinin kullanılmasıyla ilgili bir sorun yok. Ancak, statik olmayan üye işlevlerine yönelik işaretçiler, normal işlev işaretçileri gibi değildir: İşlev için örtülü bir argüman olarak iletilen bir nesne üzerinde üye işlevlerinin çağrılması gerekir. Yukarıdaki üye fonksiyonunuzun imzası,

void (aClass::*)(int, int)

kullanmayı denediğiniz türden ziyade

void (*)(int, int)

Bir yaklaşım üye işlevini oluştururken oluşabilir static Bu durumda, çağrılacak herhangi bir nesne gerektirmez ve bu türle kullanabilirsiniz. void (*)(int, int).

Sınıfınızın statik olmayan herhangi bir üyesine erişmeniz gerekiyorsa ve işlev işaretçileriyle, örneğin, işlev C arabiriminin bir parçası olduğu için, en iyi seçeneğiniz her zaman bir void* işlevinize işlev işaretçileri alarak ve üyelerinizden bir nesne alan bir yönlendirme işlevi aracılığıyla üyenizi arayın. void* ve sonra üye işlevini çağırır.

Uygun bir C ++ arabiriminde, işlev nesnelerinizin rasgele sınıf türlerini kullanması için işlevin templated argümanını almasını isteyebilirsiniz. Eğer templated bir arayüz kullanıyorsanız istenmeyen bir şey kullanmalısınız std::function<void(int, int)>: Bunlar için uygun bir şekilde değiştirilebilir işlev nesnesi oluşturabilirsiniz. std::bind().

Sınıf türü veya uygun bir şablon argümanı kullanan tip güvenli yaklaşımlar std::function<...> kullanmadan tercih edilir void* Arabirim yanlış tipte bir döküm nedeniyle hata olasılığını ortadan kaldırır.

Bir üye işlevini çağırmak için bir işlev işaretçisinin nasıl kullanılacağını açıklığa kavuşturmak için, bir örnek:

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
    fptr(context, 17, 42);
}

void non_member(void*, int i0, int i1) {
    std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}

struct foo {
    void member(int i0, int i1) {
        std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
    }
};

void forwarder(void* context, int i0, int i1) {
    static_cast<foo*>(context)->member(i0, i1);
}

int main() {
    somefunction(&non_member, 0);
    foo object;
    somefunction(&forwarder, &object);
}

92
2017-09-30 16:36



Tamam, bu cevabı beğeniyorum! "Üyenizi void * 'den bir nesneyi alan ve sonra üye fonksiyonunu çağırır bir yönlendirme fonksiyonu ile çağırmak" ile neyi kastettiğinizi belirtebilir veya buna faydalı bir link paylaşabilir misiniz? Teşekkürler - Jorge Leitão
Sanırım anladım. (Gönderinizi düzenledim) Açıklama ve örnek için teşekkürler, gerçekten yararlı. Sadece onaylamak için: işaret etmek istediğim her üye işlevi için bir iletici yapmalıyım. Sağ? - Jorge Leitão
Evet, evet. Şablonları ne kadar etkili kullandığınıza bağlı olarak, farklı sınıflar ve üye işlevleri ile çalışabilen yönlendirme şablonları oluşturarak uzaklaşabilirsiniz. Bunun nasıl yapılacağı ayrı bir işlev olur, düşünürüm ;-) - Dietmar Kühl
Bu cevabı beğenmedim çünkü kullandı void*Bu da, çok kötü hatalar alabileceğiniz anlamına gelir, çünkü artık yazılmamış. - Superlokkus
@Superlokkus: Lütfen bizi bir alternatifle aydınlatır mısınız? - Dietmar Kühl


@Pete Becker'in cevabı gayet iyi ama sen geçmeden de yapabilirsin class açık bir parametre olarak function1 C ++ 11'de:

#include <functional>
using namespace std::placeholders;

void function1(std::function<void(int, int)> fun)
{
    fun(1, 1);
}

int main (int argc, const char * argv[])
{
   ...

   aClass a;
   auto fp = std::bind(&aClass::test, a, _1, _2);
   function1(fp);

   return 0;
}

54
2017-09-30 16:42



mı void function1(std::function<void(int, int)>) doğru? - Deqing
Fonksiyon argümanına bir değişken ismi vermelisiniz ve daha sonra değişken ad aslında geçtiğiniz şeydir. Yani: void function1(std::function<void(int, int)> functionToCall) ve sonra functionToCall(1,1);. Cevabı düzenlemeyi denedim ama birisi bunu herhangi bir sebepten dolayı mantıklı kılmadığını reddetti. Bir noktada tahliye olup olmadığını göreceğiz. - Dorky Engineer
@DorkyEngineer Bu oldukça garip, bence haklı olmalısın ama bu hatanın uzun zamandır fark edilmeden nasıl geçtiğini bilmiyorum. Her neyse, cevabı şimdi düzenledim. - Matt Phillips
Bunu buldum posta std :: function'dan ciddi bir performans cezası olduğunu söyleyerek. - kevin


Üye işlevine bir işaretçi, işlevin bir işaretçisinden farklıdır. Bir işaretçi aracılığıyla bir üye işlevini kullanabilmek için, ona bir işaretçi (açık bir şekilde) ve bunu uygulamak için bir nesne gerekir. Yani uygun sürümü function1 olabilir

void function1(void (aClass::*function)(int, int), aClass& a) {
    (a.*function)(1, 1);
}

ve onu aramak için:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);

37
2017-09-30 16:35



Çok teşekkür ederim. Burada parantezlerin sayıldığını öğrendim: function1 (& (aClass :: test), a) MSVC2013 ile çalışıyor ancak gcc ile çalışmıyor. gcc, sınıf adının önüne & doğrudan ihtiyaç duyar (kafa karıştırıcı buluyorum, çünkü & operatör işlevin adresini değil, sınıfın adresini alır) - kritzel_sw
düşündüm function içinde void (aClass::*function)(int, int) bir türdü, çünkü bir tür typedef void (aClass::*function)(int, int). - Olumide
@Olumide - typedef int X; bir türü tanımlar; int X; bir nesne oluşturur. - Pete Becker


2011'den beri değiştirebilirseniz function1, öyle, böyle:

#include <functional>
#include <cstdio>

using namespace std;

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

template <typename Callable>
void function1(Callable f)
{
    f(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass obj;

    // Free function
    function1(&test);

    // Bound member function
    using namespace std::placeholders;
    function1(std::bind(&aClass::aTest, obj, _1, _2));

    // Lambda
    function1([&](int a, int b) {
        obj.aTest(a, b);
    });
}

(canlı demo)

Ayrıca kırık nesne tanımınızı da düzelttiğime dikkat edin (aClass a(); bir işlevi bildirir).


0
2017-08-10 16:03





Şimdi kafalarını becermeyi bırakabilirsin. Üye işlevinin destekleyeceği sarıcı İşte mevcut düz alarak fonksiyonlar C argümanlar olarak işlev görür. thread_local Direktif burada anahtardır.

http://cpp.sh/9jhk3

// Example program
#include <iostream>
#include <string>

using namespace std;

typedef int FooCooker_ (int);

// Existing function
extern "C" void cook_10_foo (FooCooker_ FooCooker) {
    cout << "Cooking 10 Foo ..." << endl;
    cout << "FooCooker:" << endl;
    FooCooker (10);
}

struct Bar_ {
    Bar_ (int Foo = 0) : Foo (Foo) {};
    int cook (int Foo) {
        cout << "This Bar got " << this->Foo << endl;
        if (this->Foo >= Foo) {
            this->Foo -= Foo;
            cout << Foo << " cooked" << endl;
            return Foo;
        } else {
            cout << "Can't cook " <<  Foo << endl;
            return 0;
        }
    }
    int Foo = 0;
};

// Each Bar_ object and a member function need to define
// their own wrapper with a global thread_local object ptr
// to be called as a plain C function.
thread_local static Bar_* BarPtr = NULL;
static int cook_in_Bar (int Foo) {
    return BarPtr->cook (Foo);
}

thread_local static Bar_* Bar2Ptr = NULL;
static int cook_in_Bar2 (int Foo) {
    return Bar2Ptr->cook (Foo);
}

int main () {
  BarPtr = new Bar_ (20);
  cook_10_foo (cook_in_Bar);

  Bar2Ptr = new Bar_ (40);
  cook_10_foo (cook_in_Bar2);

  delete BarPtr;
  delete Bar2Ptr;
  return 0;
}

Lütfen bu yaklaşımla ilgili herhangi bir konu hakkında yorum yapın.

Diğer cevaplar arayabilir mevcut sade C fonksiyonlar: http://cpp.sh/8exun


0
2017-08-07 10:24



Yani örneğin std :: bind veya lambda kullanmak yerine örneği global bir değişkene güvenirsiniz. Bu yaklaşıma diğer cevaplara kıyasla bir avantaj göremiyorum. - super
@super, Diğer cevaplar düz alandaki mevcut işlevleri arayamaz C argümanlar olarak işlev görür. - neckTwi
Soru, bir üye fonksiyonunun nasıl çağrılacağıdır. Özgür bir işlevin geçmesi zaten soru için OP için çalışıyor. Sen de hiçbir şeyden geçmiyorsun. Burada sadece sabit kodlanmış işlevler ve global bir Foo_ işaretçisi var. Farklı bir üye işlevini çağırmak isterseniz bu ölçek nasıl olur? Temel işlevleri yeniden yazmanız veya her hedef için farklı olanları kullanmanız gerekir. - super