टेम्पलेट फ़ंक्शन पॉइंटर-टू-मेंबर्स-फ़ंक्शन के लिए काम नहीं करता है


14

हाल ही में मैंने कुछ कोड repetitions को हल करने के लिए एक टेम्पलेट फ़ंक्शन लिखा था। यह इस तरह दिख रहा है:

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(fun, *sp, args...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

int main() {
    auto a = std::make_shared<A>();
    call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}

यह कोड पूरी तरह से अच्छी तरह से काम करता है class Aजिसके लिए यह दिखता है:

class A {
public:
    void foo(int x) {

    }
};

लेकिन इस तरह से एक के लिए संकलन करने में विफल:

class A {
public:
    void foo(const int& x) {

    }
};

ऐसा क्यों है (इसके द्वारा मेरा मतलब है कि यह प्रकार को कम करने में विफल क्यों है) और कैसे (यदि यह बिल्कुल संभव है) क्या मैं इस कोड को संदर्भों के साथ काम कर सकता हूं? जीवंत उदाहरण


शायद Args&&...और std::forward?
फेस

@ user3365922 ने इसकी कोशिश की। समाधान की तरह महसूस करता है, काम नहीं करता है
बारटॉप

क्या यह और यह आपको सही दिशा में मदद नहीं करेगा ?
Gizmo

जवाबों:


3

आपका मुद्दा यह है कि आप के Argsबीच संघर्ष कटौती है :

  • R (T::*fun)(Args...)
  • Args... args

मेरा सुझाव है कि इसके साथ और अधिक सामान्य कोड (कोई R (T::*fun)(Args...)और
संस्करण R (T::*fun)(Args...) constऔर अन्य विकल्प के बीच कोई दोहराव नहीं ):

template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
                             const std::string& error,
                             F f,
                             Args&&... args)
{
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(f, *sp, std::forward<Args>(args)...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

सदस्य समारोह के सीवी-योग्यता के बारे में अच्छी बात, मुझे लगता है कि यह अब तक का सबसे अच्छा समाधान है
बार्टॉप

8

Argsप्रकारों को const&( funपैरामीटर घोषणा से) और घोषणा से गैर-संदर्भ दोनों के रूप में नहीं घटाया जा सकता है args। एक साधारण फिक्स दो अलग-अलग टेम्पलेट प्रकार पैरामीटर पैक का उपयोग करना है:

template<class T, class R, class... Args, class... DeclaredArgs>
R call_or_throw(
    const std::weak_ptr<T>& ptr,
    const std::string& error,
    R (T::*fun)(DeclaredArgs...),
    Args... args);

नकारात्मक पक्ष के रूप में, मैं खराब उपयोग के मामले में थोड़े लंबे त्रुटि संदेशों की कल्पना कर सकता हूं।


1
आप शायद चाहते हैंArgs&&... args
Jarod42

5

ध्यान दें कि टेम्प्लेट पैरामीटर Argsके प्रकार को const int&3 फंक्शन तर्क के अनुसार &A::fooघटाया जाता है, और intचौथे फंक्शन पैरामीटर के अनुसार घटाया जाता है1 । वे मेल नहीं खाते हैं और कटौती विफल हो जाती है।

आप कटौती से 4 वें पैरामीटर को बाहर कर सकते हैं , जैसे

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, 
                const std::string& error, 
                R (T::*fun)(Args...), 
                std::type_identity_t<Args>... args) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

लाइव

PS: std::type_identityC ++ 20 के बाद से समर्थित है; लेकिन इसे लागू करना काफी आसान है।


1
यह किसी भी तरह सही अग्रेषण के साथ काम करने वाला है?
बारतोप

@ बर्थॉप मुझे ऐसा लगता है। हम संदर्भ शैली को अग्रेषित करने के लिए 4 वाँ पैरामीटर बना सकते हैं, अर्थात Args&&..., फिर std::type_identityजैसे 3 पैरामीटर पर रखें R (T::*fun)(std::type_identity_t<Args>...)LIVE और LIVE
songyuanyao

@songyuanyo हाँ, लेकिन फिर यह मूल्य तर्क के लिए टूट जाएगा।
बारतोप

आप पहले से ही अपने कोड डेमो से आगे का उपयोग कर सकते हैं । यह सिर्फ "अतिरिक्त" कदम करेगा।
Jarod42
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.