এক ক্লাসে সদস্য ফাংশন সহ জেনেরিক স্টাড :: ফাংশন অবজেক্ট ব্যবহার করা


169

একটি শ্রেণির জন্য আমি একই স্টোরের কয়েকটি সদস্য ফাংশন পয়েন্টারগুলিকে একটি mapস্টোরিং std::functionঅবজেক্টে সঞ্চয় করতে চাই। তবে আমি এই কোডটি দিয়ে শুরুতেই ঠিক ব্যর্থ হয়েছি:

class Foo {
    public:
        void doSomething() {}
        void bindFunction() {
            // ERROR
            std::function<void(void)> f = &Foo::doSomething;
        }
};

আমি পাই error C2064: term does not evaluate to a function taking 0 argumentsমধ্যে xxcallobjকিছু অদ্ভুত টেমপ্লেট ইনস্ট্যান্স ত্রুটি সঙ্গে মিলিত। বর্তমানে আমি ভিজ্যুয়াল স্টুডিও 2010/2011 এর সাথে উইন্ডোজ 8 এ এবং ভিএস 10 এর সাথে উইন 7 এ কাজ করছি যা ব্যর্থ হয়। ত্রুটিটি অবশ্যই আমি কিছু অদ্ভুত সি ++ নিয়ম অনুসরণ করি না

উত্তর:


301

একটি স্থিতিশীল সদস্য ফাংশন একটি অবজেক্ট সহ কল ​​করা আবশ্যক। এটি হ'ল এটি সর্বদা স্পষ্টভাবে "এই" পয়েন্টারটিকে তার আর্গুমেন্ট হিসাবে পাস করে।

আপনার std::functionস্বাক্ষরটি উল্লেখ করে যে আপনার ফাংশনটি কোনও আর্গুমেন্ট ( <void(void)>) গ্রহণ করে না , আপনাকে অবশ্যই প্রথম (এবং একমাত্র) যুক্তি আবদ্ধ করতে হবে ।

std::function<void(void)> f = std::bind(&Foo::doSomething, this);

আপনি যদি পরামিতিগুলির সাথে কোনও ফাংশন বাঁধতে চান তবে আপনার স্থানধারক নির্দিষ্ট করতে হবে:

using namespace std::placeholders;
std::function<void(int,int)> f = std::bind(&Foo::doSomethingArgs, this, std::placeholders::_1, std::placeholders::_2);

অথবা, যদি আপনার সংকলক সি ++ 11 ল্যাম্বডাস সমর্থন করে:

std::function<void(int,int)> f = [=](int a, int b) {
    this->doSomethingArgs(a, b);
}

(আমার কাছে এখনই হাতে সি ++ 11 সক্ষম সংকলক নেই , তাই আমি এটি পরীক্ষা করতে পারি না))


1
আমি বাউস্টের উপর নির্ভরশীল না হওয়ায় আমি ল্যাম্বডা এক্সপ্রেশন ব্যবহার করব;) তবুও ধন্যবাদ!
খ্রিস্টান আইভিসেভিচ

3
@ অ্যালেক্সবি: বুস্ট.বাইন্ড স্থানধারীদের জন্য এডিএল ব্যবহার করে না, এটি তাদের একটি বেনামে নাম স্থানে রাখে।
iljarn

46
আমি বিশ্বব্যাপী ক্যাপচার এড়ানোর পরামর্শ দিচ্ছি [=] এবং যা ধরা পড়েছে তা পরিষ্কার করে তুলতে [এটি] ব্যবহার করুন (স্কট মায়ার্স - কার্যকর আধুনিক সি ++ অধ্যায় 6. আইটেম 31 - ডিফল্ট ক্যাপচার মোড
এড়ান

5
কেবলমাত্র একটি সামান্য টিপ যোগ করুন: সদস্য ফাংশন পয়েন্টারটি std::functionthisstd::function<void(Foo*, int, int)> = &Foo::doSomethingArgs
স্পষ্টতই

@ ল্যান্ডারলিউং: ফাংশনটির নাম যোগ করুন উপরে নমুনা বাক্য গঠন ঠিক করতে উপরে "চ" হিসাবে বলুন। যদি আপনার নামের প্রয়োজন না হয় তবে আপনি mem_fn (& Foo :: doSomethingArgs) ব্যবহার করতে পারেন।
Val,

80

হয় আপনার প্রয়োজন

std::function<void(Foo*)> f = &Foo::doSomething;

যাতে আপনি এটি যে কোনও পরিস্থিতিতে কল করতে পারেন বা উদাহরণস্বরূপ আপনাকে একটি নির্দিষ্ট উদাহরণকে আবদ্ধ করতে হবে this

std::function<void(void)> f = std::bind(&Foo::doSomething, this);

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

এই সংকলন, কিন্তু এটি মান? আপনি কি প্রথম যুক্তি যে গ্যারান্টিযুক্ত this?
sudo rm -rf

@ সুডর্ম-আরফস্ল্যাশ হ্যাঁ, আপনি
আর্মেন ​​সিরুনিয়ান

@ আর্মেনটিসিরুনিয়ান উত্তরটি দেওয়ার জন্য ধন্যবাদ ... আমি কোথায় এই তথ্যটি পেতে পারি?
sudo rm -rf

13

আপনার যদি শ্রেণীর উদাহরণ ব্যতীত কোনও সদস্য ফাংশন সঞ্চয় করতে হয় তবে আপনি এই জাতীয় কিছু করতে পারেন:

class MyClass
{
public:
    void MemberFunc(int value)
    {
      //do something
    }
};

// Store member function binding
auto callable = std::mem_fn(&MyClass::MemberFunc);

// Call with late supplied 'this'
MyClass myInst;
callable(&myInst, 123);

অটো ছাড়া স্টোরেজের ধরণটি কেমন হবে ? এটার মতো কিছু:

std::_Mem_fn_wrap<void,void (__cdecl TestA::*)(int),TestA,int> callable

আপনি এই ফাংশন স্টোরেজটিকে একটি স্ট্যান্ডার্ড ফাংশন বাইন্ডিংয়েও পাস করতে পারেন

std::function<void(int)> binding = std::bind(callable, &testA, std::placeholders::_1);
binding(123); // Call

অতীত এবং ভবিষ্যতের নোট: একটি পুরানো ইন্টারফেস std :: mem_func বিদ্যমান ছিল, তবে এটি অবহেলা করা হয়েছে। সদস্য ফাংশনগুলিকে কলযোগ্য বলে পয়েন্টার করার জন্য একটি প্রস্তাব বিদ্যমান, সি ++ 17 পোস্ট করুন । এটি সবচেয়ে স্বাগত হবে।


@Danh std::mem_fnছিল না মুছে ফেলা হয়েছে; অপ্রয়োজনীয় ওভারলোডগুলির একটি গুচ্ছ ছিল। অন্যদিকে std::mem_funসি ++ 11 এর সাথে অবমূল্যায়ন করা হয়েছিল এবং C ++ 17 দিয়ে সরানো হবে।
ম্যাক্স ট্রুক্সা

@ ডানহ আমি ঠিক এটিই বলছি;) প্রথম "বেসিক" ওভারলোড এখনও রয়েছে: template<class R, class T> unspecified mem_fn(R T::*);এবং এটি চলে যাবে না।
ম্যাক্স ট্রুকসা

@Danh পড়ুন ডিআর সাবধানে। 13 টির মধ্যে 12 টি ওভারলোডগুলি ডিআর দ্বারা সরানো হয়েছিল। শেষটি ছিল না (এবং এটি হবে না; সি ++ 11 বা সি ++ 14 এও নয়)।
ম্যাক্স ট্রুক্সা

1
কেন ডাউন ভোট? অন্যান্য প্রতিটি প্রতিক্রিয়া জানিয়েছে যে আপনাকে অবশ্যই শ্রেণীর উদাহরণটি আবদ্ধ করতে হবে। আপনি যদি প্রতিবিম্ব বা স্ক্রিপ্টিংয়ের জন্য একটি বাধ্যতামূলক ব্যবস্থা তৈরি করে থাকেন তবে আপনি এটি করতে চাইবেন না। এই বিকল্প পদ্ধতিটি কিছু লোকের জন্য বৈধ এবং প্রাসঙ্গিক।
গ্রেগ

ধন্যবাদ ড্যানহ, আমি সম্পর্কিত অতীত এবং ভবিষ্যতের ইন্টারফেস সম্পর্কে কিছু মন্তব্য দিয়ে সম্পাদনা করেছি।
গ্রেগ

3

দুর্ভাগ্যক্রমে, সি ++ আপনাকে কোনও অবজেক্ট এবং এর একটি সদস্য ফাংশন উল্লেখ করে সরাসরি কলযোগ্য বস্তু পেতে অনুমতি দেয় না। &Foo::doSomethingআপনি কোন সদস্য ফাংশন কিন্তু বোঝায় একটি "সদস্য ফাংশন পয়েন্টার" দেন না সংশ্লিষ্ট বস্তু।

এর চারপাশে দুটি উপায় রয়েছে, একটি হ'ল std::bindপয়েন্টারটিতে "পয়েন্টার থেকে সদস্য ফাংশন" বাঁধতে ব্যবহার করা this। অন্যটি হ'ল ল্যাম্বদা ব্যবহার করা যা thisপয়েন্টারটি ক্যাপচার করে এবং সদস্য ফাংশনটি কল করে।

std::function<void(void)> f = std::bind(&Foo::doSomething, this);
std::function<void(void)> g = [this](){doSomething();};

আমি পরেরটি পছন্দ করব

G ++ এর সাথে কমপক্ষে সদস্য ফাংশনকে এটির সাথে আবদ্ধ করার ফলে কোনও অবজেক্টের আকার তিন-পয়েন্টার হয়ে যাবে, এটিকে নির্ধারিত করার std::functionফলে গতিশীল মেমরির বরাদ্দ হবে।

অন্যদিকে, ল্যাম্বডা যা ক্যাপচার করে thisকেবল আকারে কেবল একটি পয়েন্টার, এটিকে নির্ধারণের std::functionফলে জি ++ সহ গতিশীল মেমরির বরাদ্দ হয় না।

যদিও আমি অন্যান্য সংকলকগুলির সাথে এটি যাচাই করে নিছি, তবে আমি সন্দেহ করি যে সেখানে অনুরূপ ফলাফল পাওয়া যাবে।


1

আপনি যদি হুডের নীচে কম জেনেরিক এবং আরও সুনির্দিষ্ট নিয়ন্ত্রণ চান তবে আপনি ফান্টেক্টর ব্যবহার করতে পারেন। ক্লাস থেকে অন্য ক্লাসে এপিআই বার্তা ফরোয়ার্ড করার জন্য আমার উইন 32 এপি সহ উদাহরণ।

IListener.h

#include <windows.h>
class IListener { 
    public:
    virtual ~IListener() {}
    virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
};

Listener.h

#include "IListener.h"
template <typename D> class Listener : public IListener {
    public:
    typedef LRESULT (D::*WMFuncPtr)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

    private:
    D* _instance;
    WMFuncPtr _wmFuncPtr; 

    public:
    virtual ~Listener() {}
    virtual LRESULT operator()(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) override {
        return (_instance->*_wmFuncPtr)(hWnd, uMsg, wParam, lParam);
    }

    Listener(D* instance, WMFuncPtr wmFuncPtr) {
        _instance = instance;
        _wmFuncPtr = wmFuncPtr;
    }
};

Dispatcher.h

#include <map>
#include "Listener.h"

class Dispatcher {
    private:
        //Storage map for message/pointers
        std::map<UINT /*WM_MESSAGE*/, IListener*> _listeners; 

    public:
        virtual ~Dispatcher() { //clear the map }

        //Return a previously registered callable funtion pointer for uMsg.
        IListener* get(UINT uMsg) {
            typename std::map<UINT, IListener*>::iterator itEvt;
            if((itEvt = _listeners.find(uMsg)) == _listeners.end()) {
                return NULL;
            }
            return itEvt->second;
        }

        //Set a member function to receive message. 
        //Example Button->add<MyClass>(WM_COMMAND, this, &MyClass::myfunc);
        template <typename D> void add(UINT uMsg, D* instance, typename Listener<D>::WMFuncPtr wmFuncPtr) {
            _listeners[uMsg] = new Listener<D>(instance, wmFuncPtr);
        }

};

ব্যবহারের নীতিগুলি

class Button {
    public:
    Dispatcher _dispatcher;
    //button window forward all received message to a listener
    LRESULT onMessage(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
        //to return a precise message like WM_CREATE, you have just
        //search it in the map.
        return _dispatcher[uMsg](hWnd, uMsg, w, l);
    }
};

class Myclass {
    Button _button;
    //the listener for Button messages
    LRESULT button_listener(HWND hWnd, UINT uMsg, WPARAM w, LPARAM l) {
        return 0;
    }

    //Register the listener for Button messages
    void initialize() {
        //now all message received from button are forwarded to button_listener function 
       _button._dispatcher.add(WM_CREATE, this, &Myclass::button_listener);
    }
};

শুভকামনা এবং জ্ঞান ভাগ করে নেওয়ার জন্য সকলকে ধন্যবাদ।


0

আপনি এটি করা এড়াতে পারেন std::bind:

std::function<void(void)> f = [this]-> {Foo::doSomething();}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.