পয়েন্টার-থেকে-সদস্য-ফাংশন কনস্ট রেফের জন্য টেমপ্লেট ফাংশন কাজ করে না


14

ইদানীং কিছু কোড পুনরাবৃত্তি সমাধান করার জন্য আমি একটি টেম্পলেট ফাংশন লিখেছিলাম। দেখে মনে হচ্ছে:

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?
Fas

@ user3365922 এটি চেষ্টা করেছে। সমাধান মত মনে হচ্ছে, কাজ করে না
বার্টপ

না এই এবং এই আপনি ডান দিক সাহায্য করেছিল?
গিজমো

উত্তর:


3

আপনার সমস্যাটি হ'ল আপনার Argsমধ্যে বিরোধের ছাড় রয়েছে:

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

আমি আরো জেনেরিক কোড (মধ্যে কোন প্রতিলিপিকরণ আছে সুপারিশ R (T::*fun)(Args...)এবং
const সংস্করণ 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&তৃতীয় ফাংশন আর্গুমেন্ট অনুসারে &A::foo, এবং int4 র্থ ফাংশন প্যারামিটার অনুসারে অনুমিত হয় 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) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

লাইভ দেখান

পিএস: std::type_identityসি ++ 20 সাল থেকে সমর্থিত; তবে এটি কার্যকর করা বেশ সহজ।


1
এটি কি কোনওভাবে নিখুঁত ফরোয়ার্ডিংয়ের সাথে কাজ করবে?
বার্টোপ

@ বার্টপ আমিও তাই মনে করি। আমরা চতুর্থ প্যারামিটারটি ফরওয়ার্ডিং রেফারেন্স স্টাইল অনুসারে তৈরি করতে পারি, অর্থাৎ তৃতীয় প্যারামিটারটি পছন্দ মতো Args&&...রাখি । লাইভ এবং লাইভstd::type_identityR (T::*fun)(std::type_identity_t<Args>...)
গানেরুয়ান্যও

@ সিংগ্যুয়ানিয়েও হ্যাঁ, তবে তা মান যুক্তির জন্য ভেঙে যাবে।
বার্টপ

আপনি ইতিমধ্যে আপনার কোড ডেমো থেকে এগিয়ে ব্যবহার করতে পারেন । এটি কেবল "অতিরিক্ত" স্থানান্তর করবে।
জারোড 42
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.