如何简化C++模板调用,避免传递类成员指针的运行时开销?
如何消除类成员指针传递的运行时开销并简化模板调用
问题背景
先看原始代码:
using namespace std; struct FS { void print() { cout << "p\n"; } int s; }; template <typename T, typename F> void myInvoke(T& t, F f) { invoke(f, t) = 10; } int main(int, char**) { FS fs; myInvoke(fs, &FS::s); cout << fs.s << "\n"; }
这里将类成员指针&FS::s作为函数参数传递,会带来不必要的运行时开销,我们希望把它转为编译期的模板参数,同时简化调用,避免手动指定成员类型。
最优解决方案(C++20)
C++20的auto非类型模板参数可以直接推导成员指针的类型,完美满足需求:
#include <functional> #include <iostream> using namespace std; struct FS { void print() { cout << "p\n"; } int s; }; // 用auto自动推导成员指针的类型 template <typename T, auto f> void myInvoke(T& t) { invoke(f, t) = 30; } int main(int, char**) { FS fs; // 完全按你期望的方式调用,无需指定成员类型 myInvoke<FS, &FS::s>(fs); cout << fs.s << "\n"; // 输出30 }
这里auto f会自动推导&FS::s的类型为int FS::*,编译器在编译期就确定了成员指针,彻底消除了运行时传递的开销。
C++17兼容写法
如果需要兼容C++17,可通过decltype和辅助模板实现:
#include <functional> #include <iostream> using namespace std; struct FS { void print() { cout << "p\n"; } int s; }; // 内部实现模板 template <typename T, typename MemPtr, MemPtr f> void myInvokeImpl(T& t) { invoke(f, t) = 30; } // 辅助函数,自动推导成员指针类型 template <typename T, typename MemPtr> constexpr auto makeMyInvoke(MemPtr f) { return [](T& t) { myInvokeImpl<T, MemPtr, f>(t); }; } int main(int, char**) { FS fs; auto invokeS = makeMyInvoke<FS>(&FS::s); invokeS(fs); cout << fs.s << "\n"; // 输出30 }
这种写法需要先创建绑定后的函数对象再调用,不如C++20方案直接,优先推荐前者。
为什么原始写法需要指定typename P?
你最初的模板template <typename T, typename P, P FS::* f>中,P是成员变量的类型,C20之前编译器无法从&FS::s自动推导P,必须显式指定;而C20的auto非类型模板参数可以直接推导整个成员指针的类型,从而省去了手动指定P的步骤。
内容的提问来源于stack exchange,提问作者SupAl
相关产品推荐
相关产品推荐

