ฟังก์ชั่นการใช้ตัวชี้ไปยังวิธีการโดยไม่คำนึงถึงความมั่นคง

Aug 19 2020

ฉันต้องการใช้ฟังก์ชันทั่วไปที่จะอ้างอิงถึงออบเจ็กต์และตัวชี้ไปยังฟังก์ชันสมาชิกและเรียกใช้ อย่างไรก็ตามฉันไม่สามารถทำได้เมื่อคลาสของฉันมีทั้งวิธี const และ non-const เนื่องจากฉันต้องการให้โอเวอร์โหลดสองครั้ง:

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...))
{
    return (object.*method)(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...) const)
{
    return (object.*method)(Us{}...);
}

มีวิธีใดบ้างในการเขียนฟังก์ชันเทมเพลตเพียง 1 ฟังก์ชันที่จะยอมรับทั้งตัวชี้วิธี const และ non-const ดังนั้นฉันจึงไม่ต้องเขียนโค้ดซ้ำ ฉันใช้ C ++ 14

สำหรับภาพที่กว้างขึ้นสิ่งที่ฉันต้องการบรรลุในท้ายที่สุดคือส่งผ่านพารามิเตอร์ที่ 3 ซึ่งเป็นบัฟเฟอร์ข้อมูลที่จะแยกอาร์กิวเมนต์ของวิธีการ - ด้วยเหตุนี้ฟังก์ชันเทมเพลตจึงจัดการกับมันโดยทั่วไปมากที่สุด

คำตอบ

2 TedLyngmo Aug 19 2020 at 19:58

นี่คือ C ++ 14 std::functionทางเลือกโดยไม่ต้องใช้

สิ่งที่ฉันต้องการบรรลุในที่สุดคือส่งผ่านพารามิเตอร์ที่ 3 ซึ่งเป็นบัฟเฟอร์ข้อมูลที่จะแยกอาร์กิวเมนต์ของวิธีการ - ด้วยเหตุนี้ฟังก์ชันเทมเพลตจึงจัดการกับมันโดยทั่วไปมากที่สุด

สิ่งที่คุณใช้ในไซต์การโทรจะถูกส่งต่ออย่างสมบูรณ์ที่นี่:

template<typename Class, typename Func, typename... Args>
decltype(auto) callMethod_impl(Class& object, Func method, Args&&... args) {
    return (object.*method)(std::forward<Args>(args)...);
}


template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(Class& object, Ret(Class::*method)(Us...), Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(const Class& object, Ret(Class::*method)(Us...) const, Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

การสาธิต

หากคุณต้องการRetในcallMethod_implเพียงเพิ่มเป็นพารามิเตอร์แม่แบบและเรียกมันเช่นcallMethod_impl<Ret>(...)จากcallMethodoverloads ( Demo )

3 JanSchultke Aug 19 2020 at 17:28

คำตอบสั้น ๆ คืออย่าใช้สิ่งนี้ด้วยตัวเองมันได้ดำเนินการให้คุณแล้วในรูปแบบของstd::invoke:

#include <functional>

struct A {
    void foo(int x);
    void bar(int x) const;
};

void example() {
    A a;
    std::invoke(&A::foo, a, 3); 
    std::invoke(&A::bar, a, 3); 
}

เมื่อเห็นว่าคุณได้เพิ่มแท็ก C ++ 14 ในการย้อนหลังเอกสารของstd::invokeมีตัวอย่างการนำไปใช้งานที่คุณสามารถใช้ในโครงการของคุณได้

1 Jodocus Aug 19 2020 at 17:59

หากไม่มีstd::invokeคุณสามารถแก้ไขปัญหาทั่วไปได้มากหรือน้อยขึ้นอยู่กับการลบประเภท การใช้std::functionช่วยให้คุณสามารถสร้างฟังก์ชันพร็อกซีเพื่อปฏิบัติต่อฟังก์ชันของคุณได้อย่างเท่าเทียมกัน:

#include <iostream>
#include <functional>

template<typename Ret, typename ...Us>
Ret callMethod_impl(std::function<Ret(Us...)> f) {
    // common implementation here
    return f(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...)) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(const Class &object, Ret (Class::*method)(Us...) const) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

struct Foo {
    int bar() const { return 1; }
    float bar() { return 2.1f; };
};

int main() {
    Foo f;
    std::cout << callMethod(f, &Foo::bar) << std::endl;
}

โปรดทราบว่าvolatileฟังก์ชั่น (+ ชุดค่าผสม) ไม่ได้รับการปฏิบัติที่นี่ แต่บางทีคุณอาจไม่ต้องการความสมบูรณ์แบบทั่วไป แต่เป็นวิธีการแก้ปัญหา