ฟังก์ชั่นแม่แบบไม่ทำงานสำหรับตัวชี้ไปยังสมาชิกฟังก์ชั่นการอ้างอิง const


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 พยายาม ให้ความรู้สึกเหมือนการแก้ปัญหาไม่ได้ทำงาน
bartop

จะไม่นี้และนี้ช่วยให้คุณในทิศทางที่เหมาะสมหรือไม่
Gizmo

คำตอบ:


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

จุดที่ดีเกี่ยวกับ CV-คุณสมบัติของสมาชิกฟังก์ชันผมคิดว่านี่เป็นทางออกที่ดีที่สุดเพื่อให้ห่างไกล
bartop

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 4 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_identityรองรับตั้งแต่ C ++ 20; แต่มันค่อนข้างง่ายที่จะใช้


1
มันจะทำงานกับการส่งต่อที่สมบูรณ์แบบได้อย่างไร?
bartop

@ บาร์ทฉันคิดอย่างนั้น เราสามารถทำให้พารามิเตอร์ที่ 4 ที่สอดคล้องกับรูปแบบการอ้างอิงการส่งต่อคือArgs&&...แล้วใส่std::type_identityในพารามิเตอร์ที่ 3 R (T::*fun)(std::type_identity_t<Args>...)เช่น สดและสด
songyuanyao

@songyuanyo ใช่ แต่แล้วมันก็จะแตกสำหรับการโต้แย้งค่า
bartop

แล้วคุณสามารถใช้ไปข้างหน้าจากรหัสของคุณสาธิต มันจะทำการเคลื่อนไหว "พิเศษ"
Jarod42
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.