ฟังก์ชั่นการใช้ตัวชี้ไปยังวิธีการโดยไม่คำนึงถึงความมั่นคง
ฉันต้องการใช้ฟังก์ชันทั่วไปที่จะอ้างอิงถึงออบเจ็กต์และตัวชี้ไปยังฟังก์ชันสมาชิกและเรียกใช้ อย่างไรก็ตามฉันไม่สามารถทำได้เมื่อคลาสของฉันมีทั้งวิธี 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 ซึ่งเป็นบัฟเฟอร์ข้อมูลที่จะแยกอาร์กิวเมนต์ของวิธีการ - ด้วยเหตุนี้ฟังก์ชันเทมเพลตจึงจัดการกับมันโดยทั่วไปมากที่สุด
คำตอบ
นี่คือ 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>(...)
จากcallMethod
overloads ( Demo )
คำตอบสั้น ๆ คืออย่าใช้สิ่งนี้ด้วยตัวเองมันได้ดำเนินการให้คุณแล้วในรูปแบบของ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มีตัวอย่างการนำไปใช้งานที่คุณสามารถใช้ในโครงการของคุณได้
หากไม่มี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
ฟังก์ชั่น (+ ชุดค่าผสม) ไม่ได้รับการปฏิบัติที่นี่ แต่บางทีคุณอาจไม่ต้องการความสมบูรณ์แบบทั่วไป แต่เป็นวิธีการแก้ปัญหา