যেখানে কোনও ফ্রি ফাংশন আশা করা যায় সেখানে আমি কীভাবে কোনও সদস্য ফাংশনটি পাস করতে পারি?


122

প্রশ্নটি নিম্নলিখিত: কোডের এই অংশটি বিবেচনা করুন:

#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;
}

আমি কীভাবে aএর aClass::testযুক্তি হিসাবে ব্যবহার করতে পারি function1? আমি এটি করতে আটকে আছি।

আমি ক্লাসের কোনও সদস্যকে অ্যাক্সেস করতে চাই।


1
এই উত্তরটি কটাক্ষপাত stackoverflow.com/questions/2402579/... এবং এই সি ++ প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী parashift.com/c++-faq/pointers-to-members.html
amdn

16
এটি একেবারে একটি সদৃশ নয় (অন্তত কোনও নির্দিষ্ট লিঙ্কযুক্ত প্রশ্নের সাথে নয়)। এই প্রশ্নটি কীভাবে কোনও সদস্যকে কীভাবে কোনও ফাংশনের পয়েন্টার হিসাবে ঘোষণা করতে হয়; এটি কীভাবে পরামিতি হিসাবে অ স্থিতিশীল সদস্য ফাংশনে একটি পয়েন্টারটি পাস করতে হয়।
কার্লুভা

উত্তর:


144

ফাংশন পয়েন্টার ব্যবহার করে কোনও সমস্যা নেই। যাইহোক, অ স্থিতিশীল সদস্য ফাংশনগুলিতে পয়েন্টারগুলি সাধারণ ফাংশন পয়েন্টারগুলির মতো নয়: ফাংশনের অন্তর্নিহিত যুক্তি হিসাবে পাস হওয়া কোনও বস্তুর উপরে সদস্য ফাংশনগুলি কল করা দরকার। উপরে আপনার সদস্য ফাংশনের স্বাক্ষর এইভাবে

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

আপনি যে ধরণের ব্যবহার করার চেষ্টা করছেন তার চেয়ে বরং

void (*)(int, int)

একটি পদ্ধতির সদস্য ফাংশনটি তৈরির staticক্ষেত্রে এমন কোনও বিষয় থাকতে পারে যাতে এটির জন্য কোনও বস্তুর প্রয়োজন হয় না এবং আপনি এটি টাইপ দিয়ে ব্যবহার করতে পারেন void (*)(int, int)

আপনার ক্লাসের কোনও অ-স্থিতিশীল সদস্যের অ্যাক্সেসের প্রয়োজন হলে এবং আপনাকে ফাংশন পয়েন্টারগুলির সাথে লেগে থাকা প্রয়োজন, উদাহরণস্বরূপ, ফাংশনটি একটি সি ইন্টারফেসের অংশ হিসাবে, আপনার সর্বোত্তম বিকল্পটি সর্বদা void*আপনার ফাংশনটিতে ফাংশন পয়েন্টার এবং কল গ্রহণের মাধ্যমে একটি পাস করা হয় আপনার সদস্য একটি ফরোয়ার্ডিং ফাংশন মাধ্যমে যা একটি থেকে একটি বস্তু গ্রহণ করে void*এবং তারপর সদস্য ফাংশন কল।

একটি যথাযথ সি ++ ইন্টারফেসে আপনি নিজের ফাংশনটি স্বেচ্ছাসেবী শ্রেণীর ধরণের ব্যবহারের জন্য ফাংশন অবজেক্টগুলির জন্য টেম্পলেটযুক্ত যুক্তি গ্রহণ করতে চান। যদি কোনও টেম্প্লেটড ইন্টারফেস ব্যবহার করা অযাচিত হয় তবে আপনার এমন কিছু ব্যবহার করা উচিত std::function<void(int, int)>: আপনি এগুলির জন্য উপযুক্ত কলযোগ্য ফাংশন অবজেক্ট তৈরি করতে পারেন, যেমন, ব্যবহার করে std::bind()

শ্রেণীর ধরণের বা একটি উপযুক্ত জন্য টেম্পলেট আর্গুমেন্ট ব্যবহার করে টাইপ-নিরাপদ পন্থা ইন্টারফেস std::function<...>ব্যবহারের চেয়ে পছন্দনীয় void*কারণ তারা ভুল ধরণের একটি কাস্টের কারণে ত্রুটির সম্ভাবনাগুলি সরিয়ে দেয়।

সদস্য ফাংশনটি কল করতে কীভাবে কোনও ফাংশন পয়েন্টার ব্যবহার করবেন তা স্পষ্ট করার জন্য, এখানে একটি উদাহরণ রয়েছে:

// 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);
}

ঠিক আছে, আমি এই উত্তরটি পছন্দ করি! আপনি দয়া করে "ফরোয়ার্ডিং ফাংশনের মাধ্যমে আপনার সদস্যকে কল করুন যা শূন্য * * এর মাধ্যমে কোনও বস্তু গ্রহণ করে এবং সদস্য ফাংশনটি কল করে" তার অর্থ কী তা নির্দিষ্ট করতে পারেন, বা এর কোনও দরকারী লিঙ্ক ভাগ করে নিতে পারেন? ধন্যবাদ
জর্জি লিটাও

আমি মনে করি এটি পেয়েছি। (আমি আপনার পোস্ট সম্পাদনা করেছি) ব্যাখ্যা এবং উদাহরণের জন্য ধন্যবাদ, সত্যই সহায়ক। কেবলমাত্র নিশ্চিত করতে: প্রতিটি সদস্য ফাংশনের জন্য যা আমি নির্দেশ করতে চাই, আমাকে একটি ফরোয়ার্ডার তৈরি করতে হবে। রাইট?
জর্জি লিটাও

ভাল, হ্যাঁ, ধরনের। আপনি টেম্পলেটগুলি কতটা কার্যকর ব্যবহার করছেন তার উপর নির্ভর করে আপনি ফরোয়ার্ডিং টেম্পলেটগুলি তৈরি করতে পারেন যা বিভিন্ন শ্রেণি এবং সদস্য ফাংশন নিয়ে কাজ করতে পারে। এটি কীভাবে করা যায় তা একটি পৃথক ফাংশন হবে, আমি
ভাবব

1
আমি এই উত্তরটি অপছন্দ করি কারণ এটি ব্যবহার করে void*, যার অর্থ আপনি খুব বাজে বাগ পেতে পারেন, কারণ এটি আর পরীক্ষা করা যায় না।
সুপারলোক্কাস

@ সুপারলক্কাস: আপনি কি কোনও বিকল্প দিয়ে আমাদের আলোকিত করতে পারেন?
Dietmar Kühl

88

@ পিট বেকারের উত্তর ঠিক আছে তবে আপনি সি ++ 11 classতে সুস্পষ্ট পরামিতি হিসাবে উদাহরণটি না দিয়েই এটি করতে পারেন function1:

#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;
}

1
কি void function1(std::function<void(int, int)>)সঠিক?
Deqing

2
আপনাকে ফাংশন আর্গুমেন্টকে একটি ভেরিয়েবলের নাম দিতে হবে এবং তারপরে ভেরিয়েবলের নামটি আসলে আপনি পাস করেন। সুতরাং: void function1(std::function<void(int, int)> functionToCall)এবং তারপর functionToCall(1,1);। আমি উত্তরটি সম্পাদনা করার চেষ্টা করেছি তবে কেউ কোনও কারণে এটির কোনও অর্থ বোধ করে না বলে এটি প্রত্যাখ্যান করেছে। এটি দেখতে পাওয়া যাবে যে এটি কোনও পর্যায়ে উত্সাহিত হয়েছে কিনা।
ডর্কী ইঞ্জিনিয়ার

1
@ ডর্কি ইঞ্জিনিয়ার এটি বেশ অদ্ভুত, আমি মনে করি আপনার অবশ্যই সঠিক হতে হবে তবে আমি জানি না যে এতক্ষণ কীভাবে ত্রুটি নজরে পড়েছিল। যাইহোক, আমি এখনই উত্তরটি সম্পাদনা করেছি।
ম্যাট ফিলিপস

2
আমি এই পোস্টটি বলেছিলাম যে এখানে স্ট্যান্ড :: ফাংশন থেকে একটি কঠোর পারফরম্যান্স জরিমানা রয়েছে।
কেভিন

2
@ কেভিন, আপনি নিজের মন্তব্যটি সংশোধন করতে চাইতে পারেন, কারণ পোস্টের উত্তরগুলি বেঞ্চমার্কে একটি ত্রুটি দেখিয়েছিল।
জর্জি লিটাও

54

সদস্য ফাংশনে একটি পয়েন্টার ফাংশন থেকে পয়েন্টার থেকে পৃথক। কোনও পয়েন্টারের মাধ্যমে সদস্য ফাংশনটি ব্যবহার করার জন্য আপনাকে এটিতে একটি পয়েন্টার (স্পষ্টতই) এবং এটি প্রয়োগ করার জন্য কোনও অবজেক্টের প্রয়োজন। সুতরাং উপযুক্ত সংস্করণ function1হবে

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

এবং এটি কল করতে:

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

1
আপনাকে অনেক ধন্যবাদ. আমি সবেমাত্র জানতে পেরেছি যে বন্ধনীগুলি এখানে গণনা করছে: ফাংশন 1 (& (aClass :: পরীক্ষা), ক) এমএসভিসি ২০১৩ এর সাথে কাজ করে তবে জিসিসি দিয়ে নয়। ক্লাসের নামের সামনে জিসিসি-র সরাসরি প্রয়োজন (যা আমি বিভ্রান্তি বোধ করি, কারণ & অপারেটর ক্লাসের নয়, ফাংশনের ঠিকানা নিয়ে থাকে)
kritzel_sw

আমি ভেবেছিলাম functionমধ্যে void (aClass::*function)(int, int)একটি টাইপ ছিল, কারণ এটি একটি প্রকার typedef void (aClass::*function)(int, int)
ওলুমাইড

@ অলুমাইড - typedef int X;একটি ধরণের সংজ্ঞা দেয়; int X;একটি বস্তু তৈরি করে।
পিট বেকার

11

২০১১ সাল থেকে, আপনি যদি পরিবর্তন করতে পারেন তবে function1এটি করুন:

#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);
    });
}

( লাইভ ডেমো )

লক্ষ্য করুন যে আমি আপনার ভাঙা বস্তুর সংজ্ঞা স্থির করেছি ( aClass a();একটি ফাংশন ঘোষণা করে)।


2

আমি অনুরূপ একটি প্রশ্ন জিজ্ঞাসা করেছি ( সি ++ ওপেনফ্রেমওয়ার্কগুলি অন্যান্য শ্রেণি থেকে অকার্যকর হয়ে উঠেছে ) তবে আমি যে উত্তরটি পেয়েছি তা স্পষ্ট ছিল তাই এখানে ভবিষ্যতের রেকর্ডগুলির জন্য ব্যাখ্যা:

এটি হিসাবে স্ট্যান্ড :: ফাংশনটি ব্যবহার করা আরও সহজ:

 void draw(int grid, std::function<void()> element)

এবং তারপরে কল করুন:

 grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

বা আরও সহজ:

  grid.draw(12, [&]{a.draw()});

যেখানে আপনি এমন একটি ল্যাম্বডা তৈরি করেন যা সেই বস্তুকে রেফারেন্সের মাধ্যমে এটি ক্যাপচার বলে


1
প্রদত্ত যে এটি সিপিপিকে ট্যাগ করা হয়েছে বলে আমি মনে করি এটিই সবচেয়ে পরিষ্কার সমাধান। আমরা একটি ধ্রুবক রেফারেন্স করতে ব্যবহার করতে পারে std::functionমধ্যে drawপ্রতি কলের তে এটি কপি এর যদিও পরিবর্তে।
সোহাইব

2
স্থানধারকটি উদাহরণটিতে ব্যবহার করা উচিত নয় কারণ ফাংশনটি কোনও প্যারাম নেয় না।
ক্রোজ

1

আমি সদস্যকে স্থিতিশীল এবং সমস্ত কাজ হিসাবে ফাংশন করেছিলাম:

#include <iostream>

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

void function1(int a,int b,void function(int, int))
{
    function(a, b);
}

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

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

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

    getchar();return 0;
}

শুধুমাত্র মূল প্রশ্নের সমাধান করবেন না "আমি কীভাবে a এর aClass :: পরীক্ষাটি ফাংশন 1-এর আর্গুমেন্ট হিসাবে ব্যবহার করতে পারি?" তবে ক্লাসের বাহ্যিক কোড ব্যবহার করে শ্রেণীর ব্যক্তিগত ভেরিয়েবলগুলি সংশোধন করা
বাঞ্ছন

1

এই অবিশ্বাস্য সহজ সমাধানটি কেন পাস করা হয়েছে তা নিশ্চিত নয়:

#include <stdio.h>

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

template<class C>
void function1(void (C::*function)(int, int), C& c)
{
    (c.*function)(1, 1);
}
void function1(void (*function)(int, int)) {
  function(1, 1);
}

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

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

    function1(&test);
    function1<aClass>(&aClass::aTest, a);
    return 0;
}

আউটপুট:

1 - 1 = 0
1 + 1 = 2

0

আপনি আসলে উদাহরণস্বরূপ ব্যবহার করতে হবে না থাকে তাহলে a (ie আপনি এটা @mathengineer মত 'র স্ট্যাটিক করতে পারেন উত্তর ) আপনি কেবল একটি অ ক্যাপচার ল্যামডা মধ্যে পাস করতে পারেন। (যা পয়েন্টার ফাংশন ক্ষয়)


#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);
}

int main()
{
   //note: you don't need the `+`
   function1(+[](int a,int b){return aClass{}.aTest(a,b);}); 
}

Wandbox


দ্রষ্টব্য: যদি aClassনির্মাণ করা ব্যয়বহুল হয় বা এর পার্শ্ব প্রতিক্রিয়া থাকে, তবে এটি ভাল উপায় নাও হতে পারে।


-1

আপনি এখন মাথা ঠেকানো বন্ধ করতে পারেন। প্লেইন সি ফাংশনকে আর্গুমেন্ট হিসাবে গ্রহণ করে বিদ্যমান ফাংশনগুলিকে সমর্থন করার জন্য এখানে সদস্য ফাংশনের মোড়ক দেওয়া আছে । নির্দেশ এখানে মূল কী।thread_local

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;
}

এই পদ্ধতির সাথে যে কোনও বিষয়ে মন্তব্য করুন।

অন্যান্য উত্তরগুলি বিদ্যমান প্লে Cফাংশনগুলিতে কল করতে ব্যর্থ : http://cpp.sh/8exun


3
সুতরাং std :: বাঁধাই বা ল্যাম্বডা ব্যবহার না করে উদাহরণটি মোড়ানোর জন্য আপনি বিশ্বব্যাপী ভেরিয়েবলের উপর নির্ভর করেন। অন্যান্য উত্তরগুলির তুলনায় আমি এই পদ্ধতির কোনও সুবিধা দেখতে পাচ্ছি না।
সুপার

@ সুপার, অন্যান্য উত্তরগুলি সরল Cফাংশনগুলিকে যুক্তি হিসাবে গ্রহণ করে বিদ্যমান ফাংশনগুলিকে কল করতে পারে না ।
নেকেটভি

2
প্রশ্নটি কীভাবে একটি সদস্য ফাংশন কল করতে হয়। কোনও ফ্রি ফাংশনে উত্তীর্ণ হওয়া ইতিমধ্যে প্রশ্নের ওপিতে কাজ করছে। আপনিও কোনও কিছুতেই পাস করছেন না। এখানে কেবলমাত্র হার্ড কোডিং ফাংশন এবং একটি বিশ্বব্যাপী Foo_ পয়েন্টার রয়েছে। আপনি যদি কোনও ভিন্ন সদস্য ফাংশন কল করতে চান তবে এই স্কেলটি কীভাবে হবে? আপনাকে অন্তর্নিহিত ফাংশনগুলি আবার লিখতে হবে বা প্রতিটি লক্ষ্যের জন্য আলাদা আলাদা ব্যবহার করতে হবে।
সুপার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.